#!/bin/bash
#==============================================================#
# 脚本名     :   DMShellInstall
# 创建时间   :   2022-11-03 10:02:09
# 更新时间   :   2023-01-03 00:57:18
# 描述      :   达梦数据库一键安装脚本（单机/数据守护/DSC）
# 路径      :   /soft/DMShellInstall
# 版本      :   1.0.0
# 作者      :   Lucifer(pc1107750981@163.com) yuanzijian(yzj@dameng.com)
# Copyright (C) 2021-2022 Pengcheng Liu       Zijian yuan
#==============================================================#

#==============================================================#
#                         全局变量                              #
#==============================================================#
## 数据库的节点号，默认为 1
node_num=1
## 安装脚本以及软件包存放位置
software_dir=$(dirname "$(readlink -f "$0")")
## 达梦数据库安装日志
dminstalllog=$software_dir/print_dm_install_$(date +"20%y%m%d%H%M%S").log
## 系统当前时间
current=$(date +%Y%m%d%H%M%S)
## 操作系统版本，只获取 redhat/oracle linux/centos/麒麟V10，其他国产操作系统无法获取，都一律按照 redhat7 语法
rels=$(cat /etc/system-release)
os_version=$(echo "${rels#*release}" | awk '{print $1}' | cut -f 1 -d '.' | cut -c-3)
##服务器磁盘列表
declare -a disk_list_arr
all_disk=$(lsblk -l | awk '/^sd|vd/{print $1}' | sed -n '/[0-9]$/!p')
## 主机名
hostname=
## 主机用户dmdba密码，默认 Dameng@123
dm_passwd=Dameng@123
## 达梦数据库名称，默认 DAMENG
db_name=DAMENG
## 实例名，默认 DMSERVER
instance_name=DMSERVER
## 数据文件使用的簇大小(32)，可选值：16, 32, 64，单位：页
extent_size=32
## 数据页大小(32)，可选值：4, 8, 16, 32，单位：K
page_size=32
## 大小写敏感(Y)，可选值：Y/N，1/0
typeset -u case_sensitive
case_sensitive=Y
## 字符集(1)，可选值：0[GB18030]，1[UTF-8]，2[EUC-KR]
charset=1
## VARCHAR类型长度是否以字符为单位(N)，可选值：Y/N，1/0
typeset -u length_in_char
length_in_char=N
## 日志文件大小(1024)，单位为：M，范围为：256M ~ 2G
log_size=1024
## 是否开启归档模式，默认开启
typeset -u enable_arch
enable_arch=Y
## 归档空间大小，单位M，默认值 102400M
space_limit=102400
## 监听端口号(5236)
port_num=5236
## 达梦数据库SYSDBA用户密码，默认 SYSDBA
sysdba_pwd=SYSDBA
## 达梦软件安装目录，默认 /dm
env_dm_home=/dm
## 数据库存放目录，默认 /dmdata
data_dir=/dmdata
## 数据库备份目录，默认 /dmbak
backup_dir=/dmbak
## 数据库归档目录，默认 /dmarch
arch_dir=/dmarch
## 数据库用户脚本目录，默认 /home/dmdba/scripts
scripts_dir=/home/dmdba/scripts
## 数据库备份模式，1是全备，2是增量，默认 2
backup_mode=2
## 时间服务器IP
timeserver_ip=
## 仅配置操作系统，不安装达梦软件以及数据库，默认为 N
typeset -u only_conf_os
only_conf_os=N
## 仅安装达梦数据库软件，不创建数据库，默认为 N
typeset -u install_until_db
install_until_db=N
## 仅初始化数据库，默认为 N
typeset -u only_init_db
only_init_db=N
## 建好数据库后，优化数据库，默认为 Y
typeset -u optimize_db
optimize_db=Y
## 脚本仅优化数据库，默认为 N
typeset -u only_optimize_db
only_optimize_db=N
## 优化数据库时，内存占比百分数，默认为 80
mem_percent=80
##并发量较高的OLTP类型系统此参数设置为8，并发量不高的一般业务系统和OLAP类的系统此参数设置为1
sort_mode=0
## 达梦安装镜像名称，默认为空
dm_iso_name=
## 达梦密钥路径
key_path=
## 安装模式
typeset -l dm_install_mode

#==============================================================#
#                    数据守护 && DSC 部署全局变量                #
#==============================================================#
## 主机名，心跳IP，业务IP 数组定义
declare -a wc_hostname_arr
declare -a wc_pubip_arr
declare -a wc_malip_arr
## 操作系统 ISO 是否需要挂载，默认为 Y
typeset -u os_iso
os_iso=Y

#==============================================================#
#                         数据守护全局变量                      #
#==============================================================#
## 守护进程组名
dw_group_name=GRP1
## MAL 监听端口，默认为 5336
mal_port_num=5336
## 守护进程端口，默认为 5436
mal_dw_port_num=5436
## 实例监听守护进程端口，默认为 5536
mal_inst_dw_port_num=5536
## 数据守护切换模式，默认为手动
typeset -u dw_mode
dw_mode=MANUAL

#==============================================================#
#                         DSC 全局变量                          #
#==============================================================#
##定义DSC集群db_name
typeset -u dsc_db_name
dsc_db_name=DSC
##定义节点实例名前缀，建议和DB_NAME保持一致
typeset -l dsc_ep_name
dsc_ep_name=DSC
##是否格式化共享存储盘
typeset -u format_disk
format_disk=N
##需要过滤的磁盘
typeset -l fil_disk
##定义dcr磁盘
typeset -l dcr_disk
dcr_disk=
##定义vote磁盘
typeset -l vote_disk
vote_disk=
##定义redo日志磁盘
typeset -l log_disk
log_disk=
##定义归档磁盘
typeset -l arch_disk
arch_disk=
##定义数据磁盘
typeset -l data_disk
data_disk=
##定义磁盘数组
declare -a disk_arr
##定义asm磁盘数组
declare -a asm_disk_arr
##定义ASM实例密码
asm_pwd=Welcome1
##定义css_dcr_ep_port端口
css_dcr_ep_port=9341
##定义asm_dcr_ep_port端口
asm_dcr_ep_port=9351
##定义db_dcr_ep_port端口
db_dcr_ep_port=9741
##定义asvrmal_mal_port端口
asvrmal_mal_port=7236
##定义多路径参数
typeset -u multipath
multipath=N
##是否配置本地归档
typeset -u is_local_arch
is_local_arch=N
#==============================================================#
#                          UPPERCASE                           #
#==============================================================#
function upper() {
  echo "${1^^}"
}

#==============================================================#
#                          LOWERCASE                           #
#==============================================================#
function lower() {
  echo "${1,,}"
}

#==============================================================#
#                          日志输出颜色                        #
#==============================================================#
function color_printf() {
  local color=$1
  local res='\E[0m'
  case $color in
  "red")
    color='\E[31m'
    ;;
  "green")
    color='\E[1;32m'
    ;;
  "blue")
    color='\E[34m'
    ;;
  "pink")
    color='\E[35m'
    ;;
  *)
    color='\E[0m'
    ;;
  esac
  printf "$color%-20s %-30s %-50s$res\n" "$2" "$3" "$4"
}

#==============================================================#
#                          打印日志                             #
#==============================================================#
function log_print() {
  echo
  color_printf green "#========================================================#"
  color_printf green "$1"
  color_printf green "#========================================================#"
  color_printf "$2"
}

#==============================================================#
#                         备份系统文件                         #
#==============================================================#
function bak_file() {
  local file_path=$1
  # 检查文件中是否包含 "# DMBegin"
  if grep -q "# DMBegin" "$file_path"; then
    # 备份当前文件，添加时间戳后缀
    nohup cp -pr "$file_path"{,."$current"} >/dev/null 2>&1
    # 恢复原始文件，使用 ".original" 后缀的备份文件
    nohup cp -pr "$file_path"{.original,} >/dev/null 2>&1
  else
    # 备份原始文件，使用 ".original" 后缀
    nohup cp -pr "$file_path"{,.original} >/dev/null 2>&1
  fi
}
#==============================================================#
#                       定义容错函数                           #
#==============================================================#
function execute_with_retry() {
  local command="$1"
  local max_retry="$2"
  local retry_count=0
  while [ $retry_count -lt "$max_retry" ]; do
    # 执行命令
    $command
    if [ $? -eq 0 ]; then
      # 命令执行成功，退出重试循环
      break
    fi
    # 命令执行失败，等待一段时间后进行重试
    retry_count=$((retry_count + 1))
    sleep 5
  done
  if [ $retry_count -eq "$max_retry" ]; then
    # 所有重试次数已用完，命令仍然执行失败
    echo "$command 命令在 $max_retry 次重试后仍然失败。"
    exit 1
  fi
}
#==============================================================#
#                       检查 IP 是否合法                       #
#==============================================================#
function check_ip() {
  for i in ${1//,/ }; do
    ## 检查 IP 是否合法
    pattern="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
    if [[ ! $i =~ $pattern ]]; then
      echo
      color_printf red "请检查参数 [$2, $3] 的 IP 地址 $i 是否合法！"
      echo
      exit 99
    fi
    ## 检测 IP地址是否可以ping通
    if ! ping -c 2 -w 5 "$i" >/dev/null 2>&1; then
      echo
      color_printf red "请检查参数 [$2, $3] 的 IP 地址 $i 是否正确！"
      echo
      exit 99
    fi
  done
}
function check_ip_arr() {
  color_printf A "校验 $1 地址，请等待！！！"
  echo
  declare -a os_ip_arr
  os_ip_arr=()
  for i in ${1//,/ }; do
    os_ip_arr+=("$i")
  done

  ##获取本机ip地址
  machine_ips=$(ip addr | grep -E 'inet [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | grep -v '127.0.0.1' | awk '{split($2,a,"/"); print a[1]}')
  ##输入的ip地址与本机ip地址校验
  ip_check=false
  for machine_ip in ${machine_ips}; do
    if [[ $machine_ip = "${os_ip_arr[0]}" ]]; then
      ip_check=true
    fi
  done
  if [[ ${ip_check} != true ]]; then
    echo
    color_printf red "请检查参数 [$2, $3]，输入的第一个IP： ${os_ip_arr[0]} 不是当前机器IP，请重新输入!"
    echo
    exit 99
  fi

  ##校验ip地址是否合法正确
  check_ip "$1" "$2" "$3"

  ## 判断节点上限数量
  if [[ $dm_install_mode = "datawatch" ]]; then
    if [[ ${#wc_pubip_arr[@]} -lt 2 || ${#wc_pubip_arr[@]} -gt 9 ]]; then
      color_printf red "请检查参数 [$2, $3] $wc_public_ip ，至少需要 2 个节点，最多支持 9 个节点！"
      exit 99
    fi
  elif [[ $dm_install_mode = "dsc" ]]; then
    if [[ ${#wc_pubip_arr[@]} -lt 2 || ${#wc_pubip_arr[@]} -gt 16 ]]; then
      color_printf red "请检查参数 [$2, $3] $wc_public_ip ，至少需要 2 个节点，最多支持 16 个节点！"
      exit 99
    fi
  fi
}
#==============================================================#
#                 过滤系统盘，保留共享存储盘                     #
#==============================================================#
function filter_arr() {
  flag=0
  for a in ${all_disk// / }; do
    for f in ${fil_disk//,/ }; do
      if [ "$a" == "$f" ]; then
        flag=1
      fi
    done
    if [ $flag -eq 0 ]; then
      disk_list_arr+=("/dev/$a")
    else
      flag=0
    fi
  done
}
#==============================================================#
#                         获取磁盘容量                          #
#==============================================================#
function disk_storage() {
  lsblk -b -o SIZE -n "${1}" | awk '$1 >= 1024*1024*1024 {printf "%.1fG\n", $1/1024/1024/1024}'
}
#==============================================================#
#                         获取随机数                             #
#==============================================================#
function rand() {
  min=$1
  max=$(($2 - min + 1))
  num=$((RANDOM + 1000000000)) #增加一个10位的数再求余
  echo $((num % max + min))
}

#==============================================================#
#                           互信检查                             #
#==============================================================#
# 此函数检查一个或多个远程服务器是否可以通过SSH访问
# 使用方法：ssh_check <用户名> <逗号分隔的IP地址列表>
function ssh_check() {
  # 在开始循环之前将标志设置为true
  flag=true

  # 循环遍历列表中的每个IP地址并尝试进行SSH连接
  for ip in ${2//,/ }; do
    # 使用'su'命令以指定的用户身份运行'ssh'，并禁用主机密钥检查和启用公钥身份验证选项
    su - "$1" -c "ssh $ip -o PreferredAuthentications=publickey -o StrictHostKeyChecking=no  "echo 'true'"" >/dev/null 2>&1

    # 检查上一个命令（SSH尝试）的返回代码
    if [ $? -ne 0 ]; then
      # 如果对任何IP的SSH访问失败，则将标志设置为false
      flag=false
    fi
  done

  # 返回标志的值（如果成功访问所有IP，则为true；否则为false）
  echo "$flag"
}

#==============================================================#
#                            互信配置                           #
#==============================================================#
function ssh_trust() {
  # 判断是否已经安装expect工具，如果没有则自动安装
  log_print "安装 expect 软件，配置 OS 用户互信：" ""
  if ! command -v expect >/dev/null 2>&1; then
    if [ -f /etc/redhat-release ]; then
      yum install -y expect
      echo
    elif [ -f /etc/debian_version ]; then
      apt-get update
      apt-get install -y expect
    else
      echo "无法确定当前系统类型，请手动安装expect工具。" >&2
      exit 1
    fi
  fi

  local dest_user="$1" # 目标用户
  local password="$2"
  local host_list                     # 目标用户密码或SSH私钥密码
  host_list=$(echo "$3" | tr ',' ' ') # 远程主机列表，将逗号替换为空格

  if [[ $dest_user = "root" ]]; then
    local ssh_dir="/root/.ssh"
  else
    local ssh_dir="/home/$dest_user/.ssh"
  fi

  # 准备 ssh_dir 目录
  rm -rf "$ssh_dir" && mkdir -p "$ssh_dir" && chmod 700 "$ssh_dir"

  # 生成 ssh 密钥对
  ssh-keygen -t rsa -P "" -f "$ssh_dir/id_rsa" -q

  # 将公钥添加到 authorized_keys 文件中
  cat "$ssh_dir/id_rsa.pub" >>"$ssh_dir/authorized_keys" && chmod 600 "$ssh_dir/authorized_keys"

  ## 写入 IP 到 known_host
  [[ ! -f $ssh_dir/known_hosts ]] && touch "$ssh_dir"/known_hosts && chmod 644 "$ssh_dir"/known_hosts

  # 启动 ssh-agent，并将当前节点的私钥添加到 ssh-agent 中
  eval "$(ssh-agent)" >/dev/null 2>&1
  ssh-add ~/.ssh/id_rsa >/dev/null 2>&1

  for host in $host_list; do
    # 将目标主机的 IP 地址添加到 known_hosts 文件中
    if ! ssh-keygen -F "$host" >/dev/null; then
      ssh-keyscan -t rsa,dsa,ecdsa "$host" >>"$ssh_dir/known_hosts" 2>/dev/null
    fi

    # 远程拷贝 ~/.ssh 文件夹到目标主机
    /usr/bin/expect >/dev/null 2>&1 <<EOF
    spawn scp -rq "$ssh_dir" "$dest_user@$host:~"
    expect {
      "yes/no" { 
        send "yes\r";exp_continue 
      } "password" { 
        send "$password\r"
      } eof { 
        exit
      }
    }
    expect eof
EOF

    # ssh-copy-id to remote
    /usr/bin/expect >/dev/null 2>&1 <<EOF
    spawn ssh-copy-id -i "$ssh_dir/id_rsa.pub" "$dest_user@$host"
    expect {
      "yes/no" { 
        send "yes\r";exp_continue
      } "password" { 
        send "$password\r"
      } eof { 
        exit
      }
    }
    expect eof
EOF

    # 测试互信是否配置成功
    if ssh "$dest_user@$host" "echo 1" >/dev/null; then
      color_printf "" "$dest_user@$host 互信配置成功"
      echo
    else
      color_printf red "$dest_user@$host 互信配置失败"
      exit 99
    fi
  done

  # 停止ssh-agent
  eval "$(ssh-agent -k)" >/dev/null 2>&1
}

#==============================================================#
#                             帮助                              #
#==============================================================#
function help() {
  cat <<-'EOF'

 ███████   ████     ████  ████████ ██               ██  ██ ██                    ██              ██  ██
░██░░░░██ ░██░██   ██░██ ██░░░░░░ ░██              ░██ ░██░██                   ░██             ░██ ░██
░██    ░██░██░░██ ██ ░██░██       ░██       █████  ░██ ░██░██ ███████   ██████ ██████  ██████   ░██ ░██
░██    ░██░██ ░░███  ░██░█████████░██████  ██░░░██ ░██ ░██░██░░██░░░██ ██░░░░ ░░░██░  ░░░░░░██  ░██ ░██
░██    ░██░██  ░░█   ░██░░░░░░░░██░██░░░██░███████ ░██ ░██░██ ░██  ░██░░█████   ░██    ███████  ░██ ░██
░██    ██ ░██   ░    ░██       ░██░██  ░██░██░░░░  ░██ ░██░██ ░██  ░██ ░░░░░██  ░██   ██░░░░██  ░██ ░██
░███████  ░██        ░██ ████████ ░██  ░██░░██████ ███ ███░██ ███  ░██ ██████   ░░██ ░░████████ ███ ███
░░░░░░░   ░░         ░░ ░░░░░░░░  ░░   ░░  ░░░░░░ ░░░ ░░░ ░░ ░░░   ░░ ░░░░░░     ░░   ░░░░░░░░ ░░░ ░░░ 
EOF
  echo
  color_printf blue "用途: 一键部署达梦数据库[单机/数据守护/DMDSC集群]"
  echo
  color_printf blue "用法: DMShellInstall [选项] 选项值 { COMMAND | help }"
  echo
  color_printf blue "单机(Single): "
  echo
  color_printf green "-hn" "--hostname" "主机名"
  color_printf green "-dp" "--dm_passwd" "系统用户dmdba密码 [Dameng@123]"
  color_printf green "-d" "--env_dm_home" "数据库软件安装目录 [/dm]"
  color_printf green "-dd" "--data_dir" "数据库文件目录 [/dmdata]"
  color_printf green "-ad" "--arch_dir" "数据库归档目录 [/dmarch]"
  color_printf green "-bd" "--backup_dir" "数据库备份目录 [/dmbak]"
  color_printf green "-dn" "--db_name" "数据库名称 [DAMENG]"
  color_printf green "-in" "--instance_name" "数据库实例名称 [DMSERVER]"
  color_printf green "-es" "--extent_size" "数据文件簇大小 [32]"
  color_printf green "-ps" "--page_size" "数据页大小 [32]"
  color_printf green "-cs" "--case_sensitive" "字符串大小写敏感 [Y]"
  color_printf green "-c" "--charset" "数据库字符集 [1]"
  color_printf green "-lic" "--length_in_char" "VARCHAR类型长度是否以字符为单位 [N]"
  color_printf green "-ls" "--log_size" "日志文件大小，单位M [1024]"
  color_printf green "-er" "--enable_arch" "是否开启归档模式 [Y]"
  color_printf green "-sl" "--space_limit" "归档空间大小，单位M [102400]"
  color_printf green "-pn" "--port_num" "监听端口号 [5236]"
  color_printf green "-sp" "--sysdba_pwd" "数据库SYSDBA用户密码 [SYSDBA]"
  color_printf green "-bm" "--backup_mode" "数据库备份模式 [2]"
  color_printf green "-ti" "--timeserver_ip" "时间服务器IP地址"
  color_printf green "-m" "--only_conf_os" "仅配置操作系统，不安装达梦软件以及数据库 [N]"
  color_printf green "-ud" "--install_until_db" "仅安装达梦数据库软件，不创建数据库 [N]"
  color_printf green "-oid" "--only_init_db" "仅初始化数据库 [N]"
  color_printf green "-opd" "--optimize_db" "建库后自动优化数据库 [N]"
  color_printf green "-oopd" "--only_optimize_db" "脚本仅优化数据库 [N]"
  color_printf green "-mp" "--mem_percent" "优化数据库时物理内存占比 [80]"
  color_printf green "-sm" "--sort_mode" "并发量较高的OLTP数据库参数 [0]，并发量不高的一般业务和OLAP类的数据库参数参数值 [1],默认[0]"
  color_printf green "-di" "--dm_iso_name" "达梦数据库安装镜像名称"
  color_printf green "-kp" "--key_path" "达梦数据库密钥路径"
  echo
  color_printf blue "数据守护(DataWatch): "
  echo
  color_printf green "-rp" "--root_passwd" "root 用户密码"
  color_printf green "-hn" "--hostname" "主机名前缀，例如 -hn dw，每个节点主机名是dw01,dw02..."
  color_printf green "-dpi" "--wc_public_ip" "业务IP，以逗号隔开，例如 -dpi 10.211.55.101,10.211.55.102"
  color_printf green "-dmi" "--wc_mal_ip" "MAL IP，以逗号隔开，例如 -dmi 1.1.1.1,1.1.1.2"
  color_printf green "-dmoi" "--dw_monitor_ip" "监视器主机 IP"
  color_printf green "-dgn" "--dw_group_name" "数据守护组名 [GRP1]"
  color_printf green "-mpn" "--mal_port_num" "MAL 监听端口号 [5336],取值范围：1024~65535"
  color_printf green "-mdpn" "--mal_dw_port_num" "守护进程端口号 [5436],取值范围：1024~65535"
  color_printf green "-midpn" "--mal_inst_dw_port_num" "实例监听守护进程端口号 [5536],取值范围：1024~65535"
  color_printf green "-oi" "--os_iso" "是否需要挂载操作系统 ISO 镜像，脚本自动配置 YUM 源 [Y]"
  echo
  color_printf blue "DSC集群(DMDSC): "
  echo
  color_printf green "-rp" "--root_passwd" "root 用户密码"
  color_printf green "-hn" "--hostname" "主机名前缀，例如 -hn dsc，每个节点主机名是dsc01,dsc02..."
  color_printf green "-dpi" "--wc_public_ip" "业务IP，以逗号隔开，例如 -dpi 10.211.55.101,10.211.55.102"
  color_printf green "-dmi" "--wc_mal_ip" "MAL IP，以逗号隔开，例如 -dmi 1.1.1.1,1.1.1.2"
  color_printf green "-ddn" "--dsc_db_name" "数据库DB_NAME，默认值：DSC"
  color_printf green "-den" "--dsc_ep_name" "数据库每个节点的实例名前缀，默认值：DSC"
  color_printf green "-fd" "--format_disk" "是否需要格式化共享存储盘，默认不需要[N]"
  color_printf green "-dcd" "--dcr_disk" "dcr磁盘名字：/dev/sdb(多链路聚合的磁盘名字：dmdcr01)，只能传入一块磁盘"
  color_printf green "-vod" "--vote_disk" "vote磁盘名字：/dev/sdc(多链路聚合的磁盘名字：dmvote01)，只能传入一块磁盘"
  color_printf green "-lod" "--log_disk" "redo日志磁盘名字：/dev/sdd,/dev/sde(多链路聚合的磁盘名字：dmlog01,dmlog02),可以是一块盘，也可以是多块盘，如果没有划分单独的redo磁盘，可以不写"
  color_printf green "-ard" "--arch_disk" "归档日志磁盘名字：/dev/sdf,/dev/sdg(多链路聚合的磁盘名字：dmarch01,dmarch02),可以是一块盘，也可以是多块盘，如果没有划分单独的arch磁盘，可以不写"
  color_printf green "-dad" "--data_disk" "数据盘名字：/dev/sdh,/dev/sdi(多链路聚合的磁盘名字：dmdata01,dmdata02),可以是一块盘，也可以是多块盘，盘数必须大于等于 1 "
  color_printf green "-ila" "--is_local_arch" "是否配置本地归档，如果配置，默认数据库归档目录 [/dmarch]，可以由参数-ad指定具体目录,默认值 [N]"
  color_printf green "-apd" "--asm_pwd" "asm实例密码,默认值：Welcome1"
  color_printf green "-cdp" "--css_dcr_ep_port" "css通信端口,默认值：[9341],取值范围：1024~65534"
  color_printf green "-adp" "--asm_dcr_ep_port" "asm通信端口,默认值：[9351],取值范围：1024~65534"
  color_printf green "-ddp" "--db_dcr_ep_port" "db通信端口,默认值：[9741],取值范围：1024~65534"
  color_printf green "-amp" "--asvrmal_mal_port" "mal系统通信端口,默认值：[7236],取值范围：1024~65534"
  color_printf green "-mtp" "--multipath" "是否需要配置multipath多链路聚合，脚本默认不配置 [N]"
  color_printf green "-oi" "--os_iso" "是否需要挂载操作系统 ISO 镜像，脚本自动配置 YUM 源 [Y]"
  echo
  color_printf red "注意：本脚本仅用于新服务器上实施部署数据使用，请严禁在已运行数据库的主机上执行，以免发生数据丢失或者损坏，造成不可挽回的损失！！！"
  echo
  exit 0
}

#==============================================================#
#                         打印环境信息                          #
#==============================================================#
function print_sysinfo() {
  log_print "打印系统信息" ""
  ## 服务器时间
  color_printf blue "服务器时间: "
  echo
  date
  ## 操作系统版本
  echo
  color_printf blue "操作系统版本: "
  echo
  if [[ -e /etc/os-release ]]; then
    cat /etc/os-release
  elif [[ -e /etc/system-release ]]; then
    cat /etc/system-release
  elif [[ -e /etc/redhat-release ]]; then
    cat /etc/redhat-release
  fi
  ## cpu信息
  color_printf blue "cpu信息: "
  echo
  lscpu
  ## 内存信息
  echo
  color_printf blue "内存信息: "
  echo
  free -m && free -h
  ## 挂载信息
  echo
  color_printf blue "挂载信息: "
  echo
  grep </etc/fstab -E -v '^#|^*#|^$'
  ## 目录信息
  echo
  color_printf blue "目录信息: "
  echo
  df -h
}

function conf_yum() {
  local -r mountPatch=$(mount | grep -E "iso|iso" | head -n1 | awk '{print $3}')

  if [[ -z $mountPatch ]]; then
    color_printf red "操作系统 ISO 镜像未挂载，且 -oi 参数配置为 Y，请先挂载操作系统镜像！"
    exit 99
  fi

  ## 防止操作系统提示未注册
  [[ -e /etc/yum/pluginconf.d/subscription-manager.conf ]] && sed -i 's/enabled=1/enabled=0/' /etc/yum/pluginconf.d/subscription-manager.conf

  ## 备份原有的yum配置文件
  mkdir -p /etc/yum.repos.d/bak
  \cp /etc/yum.repos.d/* /etc/yum.repos.d/bak >/dev/null 2>&1

  if [[ $os_version = "6" || $os_version = "7" ]]; then
    cat <<-EOF >/etc/yum.repos.d/local.repo
[server]
name=server
baseurl=file://$mountPatch
enabled=1
gpgcheck=0
EOF
  elif [[ $os_version = "8" || $os_version = "V10" ]]; then
    cat <<-EOF >/etc/yum.repos.d/local.repo
[BaseOS]
name=BaseOS
baseurl=file://$mountPatch/BaseOS
enabled=1
gpgcheck=0
[AppStream]
name=AppStream
baseurl=file://$mountPatch/AppStream
enabled=1
gpgcheck=0
EOF
  fi
}
#==============================================================#
#                         关闭 SWAP 功能                        #
#==============================================================#
# 此函数用于禁用 Linux 系统中的 SWAP 分区
function conf_swap() {
  # 输出一条日志消息表示正在关闭 SWAP 功能
  log_print "关闭 SWAP 功能" ""

  # 获取系统中 SWAP 分区的大小并判断是否存在 SWAP 分区
  local -r swap=$(free | grep Swap | awk '{print $2}')
  if [[ -z $swap ]]; then
    # 如果不存在 SWAP 分区，则输出一条消息表示无需关闭 SWAP 功能
    color_printf yellow "系统中不存在 SWAP 分区，无需关闭 SWAP 功能！"
  else
    # 如果存在 SWAP 分区，则关闭 SWAP、备份 /etc/fstab 文件，并使用 sed 命令注释掉 /etc/fstab 文件中包含 "swap" 的行
    swapoff -a
    bak_file /etc/fstab
    sed -ri 's/.*swap.*/#&/' /etc/fstab

    # 输出当前系统内存的使用情况并检查 /etc/fstab 文件中是否有包含 "swap" 的行
    free -m
    echo
    grep </etc/fstab -v "^$" | grep "swap"
  fi
}
#==============================================================#
#                         禁用防火墙                             #
#==============================================================#
# 此函数用于禁用 Linux 系统中的防火墙
function conf_firewall() {
  # 输出一条日志消息表示正在禁用防火墙
  log_print "禁用防火墙" ""

  if [[ $os_version = "6" ]]; then
    # 如果操作系统版本为 RHEL 6，则通过 service 命令停止 iptables 和 ip6tables 服务并禁用开机启动
    if service iptables status >/dev/null 2>&1; then
      service iptables stop
      chkconfig iptables off
      service ip6tables stop
      chkconfig ip6tables off
    else
      echo "防火墙服务未启动，无需禁用！"
      return
    fi
    service iptables status
  else
    # 如果操作系统版本不是 RHEL 6，则通过 systemctl 命令停止 firewalld 服务并禁用开机启动
    if [[ $(systemctl status firewalld.service | grep -c running) -gt 0 ]]; then
      systemctl stop firewalld.service
      systemctl disable firewalld.service -q
    else
      echo "防火墙服务未启动，无需禁用！"
      return
    fi
    systemctl status firewalld -q
  fi
}
#==============================================================#
#                       禁用 selinux                            #
#==============================================================#
# 此函数用于禁用 SELinux
function conf_selinux() {
  # 输出一条日志消息表示正在禁用 SELinux，并备份 /etc/selinux/config 配置文件
  log_print "禁用 SELinux" ""
  bak_file /etc/selinux/config

  # 获取selinux状态
  status=$(getenforce)

  # 判断selinux状态
  if [[ $status == "Disabled" ]]; then
    # 如果 SELinux 已经被禁用，则输出一条消息表示 SELinux 已经被禁用
    echo "SELinux 已经被禁用"
  elif [[ $status == "Enforcing" ]]; then
    # 如果 SELinux 正在强制执行，则将其设置为禁用，并将 /etc/selinux/config 文件中的 SELINUX 属性设置为 disabled
    echo "SELinux 正在强制执行，现在将其设置为禁用"
    setenforce 0
    sed -i 's/SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
    echo "SELinux 已被设置为禁用，请重启系统以使更改生效"
  else
    # 如果 SELinux 是宽容模式，则将 /etc/selinux/config 文件中的 SELINUX 属性设置为 disabled
    echo "SELinux 是宽容模式"
    sed -i 's/SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
    echo "SELinux 已被设置为禁用，请重启系统以使更改生效"
  fi
}

#==============================================================#
#                         配置主机名                             #
#==============================================================#
function conf_hostname() {
  log_print "配置主机名" ""

  ## 配置dw和dsc主机名
  if [[ $dm_install_mode = "datawatch" || $dm_install_mode = "dsc" ]]; then
    for ((i = 1; i <= ${#wc_pubip_arr[@]}; i++)); do
      ## 将主机名加入数组中
      if ((i < 10)); then
        wc_hostname_arr[i - 1]="${hostname}0$i"
      else
        wc_hostname_arr[i - 1]="${hostname}$i"
      fi
    done
    ## 配置每个节点主机名
    hostname=${wc_hostname_arr[$((node_num - 1))]}
  fi

  # 获取当前主机名
  current_hostname=$(hostname)

  # 判断操作系统版本
  if [[ $os_version = "6" ]]; then
    # 获取当前主机名
    current_hostname=$(grep -E "HOSTNAME=" /etc/sysconfig/network | cut -d= -f2)
    bak_file /etc/sysconfig/network
    # 判断主机名是否需要修改
    if [[ $current_hostname != "$hostname" ]]; then
      # 修改主机名
      /bin/hostname "$hostname"
      sysctl kernel.hostname="$hostname"
      echo "$hostname" >/proc/sys/kernel/hostname
      sed -i "s/HOSTNAME=$current_hostname/HOSTNAME=$hostname/" /etc/sysconfig/network
    fi

    # 输出修改后的主机名
    grep -E "HOSTNAME=" /etc/sysconfig/network | cut -d= -f2
  else
    # 判断主机名是否需要修改
    if [[ $current_hostname != "$hostname" ]]; then
      # 修改主机名
      bak_file /etc/hostname
      hostnamectl set-hostname "$hostname"
    fi

    # 输出修改后的主机名
    hostnamectl
  fi
}

#==============================================================#
#                          配置时间同步                          #
#==============================================================#
function conf_ntp() {
  # 添加注释和变量
  log_print "配置时间同步" ""

  # 添加函数
  set_timezone() {
    if [[ $os_version = "6" ]]; then
      if [[ $(grep -E -c "Asia/Shanghai" /etc/sysconfig/clock) = 0 ]]; then
        bak_file /etc/sysconfig/clock
        nohup \cp -rv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime >/dev/null 2>&1
        cat <<-EOF >/etc/sysconfig/clock
ZONE="Asia/Shanghai"
EOF
      fi
    else
      timedatectl set-timezone Asia/Shanghai
    fi
  }

  stop_service() {
    if [[ $os_version = "6" ]]; then
      if [[ $(chkconfig --list | grep ntpd | grep -c on) -gt 0 ]]; then
        service ntpd stop
        chkconfig ntpd off
      fi
    else
      if [[ $(systemctl status chronyd | grep -c running) -gt 0 ]]; then
        systemctl stop chronyd.service
        systemctl disable chronyd.service -q
      fi
    fi
  }

  configure_ntpdate() {
    if [[ $timeserver_ip ]]; then
      # 添加数组
      local cron_file="/var/spool/cron/root"
      local commands=()
      if [[ ! -e $cron_file ]]; then
        touch $cron_file
      else
        bak_file $cron_file
      fi

      # 添加 if-else 语句和日志记录
      if [[ $os_version = "6" || $os_version = "7" ]]; then
        yum install -q -y ntpdate --skip-broken
        commands+=("/usr/sbin/ntpdate -u $timeserver_ip")
        commands+=("/usr/sbin/hwclock -w")
        color_printf blue "安装 ntpdate 成功"
      elif [[ $os_version = "8" || $os_version = "V10" ]]; then
        commands+=("/usr/sbin/chronyd -q \"server $timeserver_ip iburst \"")
        commands+=("timedatectl set-local-rtc 1")
      fi

      # 添加日志记录
      for command in "${commands[@]}"; do
        eval "$command" && color_printf blue "$command 成功"
      done

      # 添加帮助信息
      echo "00 01 * * * ${commands[0]}" >>$cron_file
      echo "00 01 * * * ${commands[1]}" >>$cron_file
      color_printf blue "添加 cron job 成功"
    fi
  }

  set_timezone
  stop_service
  configure_ntpdate

  # 添加日志记录
  date && echo && crontab -l
}

#==============================================================#
#               禁用透明大页&禁用NUMA&开启I/0 schedule           #
#==============================================================#
function conf_grub() {
  # 添加注释和变量
  log_print "禁用透明大页 & 禁用NUMA & 开启 I/0 schedule" ""
  local transparent_hugepage="transparent_hugepage=never"
  local elevator="elevator=deadline"
  local numa="numa=off"

  # 添加函数和日志记录
  update_parameter() {
    local parameter=$1
    if [[ $(grubby --info=ALL | grep -c "$parameter") = 0 ]]; then
      grubby --update-kernel=ALL --args="$parameter"
    fi
  }

  update_parameter $transparent_hugepage
  update_parameter $elevator
  update_parameter $numa

  grubby --info=ALL | grep -B2 -i numa

}

#==============================================================#
#                         创建DMDBA用户                         #
#==============================================================#
function create_user() {
  # 添加注释和变量
  log_print "创建 DMDBA 用户" ""
  local dinstall_group="dinstall"
  local dmdba_group="dmdba"
  local dmdba_user="dmdba"

  # 添加函数
  create_group() {
    local group=$1
    if [[ $(grep -E -c "$group" /etc/group) = 0 ]]; then
      /usr/sbin/groupadd -g "$2" "$group"
    fi
  }

  create_user() {
    local user=$1
    if [[ $(grep -E -c "$user" /etc/passwd) = 0 ]]; then
      /usr/sbin/useradd -u 56781 -g $dinstall_group -G $dmdba_group "$user"
    else
      /usr/sbin/usermod -g $dinstall_group -G $dmdba_group "$user"
    fi
  }
  create_group $dinstall_group 56781
  create_group $dmdba_group 56782
  create_user $dmdba_user
  echo "$dm_passwd" | passwd --stdin $dmdba_user
  echo
  grep </etc/passwd $dmdba_user
  echo
  id $dmdba_user
}

#==============================================================#
#                         创建安装目录                           #
#==============================================================#
# 此函数用于创建目录并配置权限
function create_dir() {

  # 检查 env_dm_home 目录是否存在
  if [ -d "$env_dm_home" ]; then
    # 删除 env_dm_home 目录下所有的文件
    if [ "$(ls -A "$env_dm_home")" ]; then
      rm -rf "${env_dm_home:?}/"*
    fi
  else
    # 创建 env_dm_home 目录
    mkdir -p "$env_dm_home"
  fi

  # 检查并重命名 data_dir、backup_dir 和 arch_dir 子目录（如果存在）
  for dir in "$data_dir" "$backup_dir" "$arch_dir"; do
    if [ -d "$dir" ]; then
      if [ -d "$dir/$(upper "$db_name")" ]; then
        mv "$dir/$(upper "$db_name")" "$dir/$(upper "$db_name")""$current"
      fi
    else
      mkdir -p "$dir"
    fi
  done

  # 如果 scripts_dir 目录不存在，则创建它
  test -d "$scripts_dir" || mkdir -p "$scripts_dir"

  # 如果 dm_install_mode 变量等于 "dsc"，则在 data_dir 目录中创建一个名为 $(upper "$dsc_db_name") 的子目录
  if [[ $dm_install_mode = "dsc" ]]; then
    mkdir -p "$data_dir"/"$(upper "$dsc_db_name")"
  fi

  # 设置所有目录的所有权和权限
  chown -R dmdba:dinstall "$env_dm_home" "$data_dir" "$backup_dir" "$arch_dir" "$scripts_dir"
  chmod -R 775 "$env_dm_home" "$data_dir" "$backup_dir" "$arch_dir" "$scripts_dir"
}

#==============================================================#
#                           配置内核参数和资源                         #
#==============================================================#
function conf_sysctl() {
  log_print "配置内核参数和资源" ""
  bak_file /etc/sysctl.conf
  params=(
    "# DMBegin"
    "fs.aio-max-nr = 1048576"
    "fs.file-max = 6815744"
    "kernel.core_pattern=$backup_dir/core.%e.%p.%t"
    "kernel.shmmni = 4096"
    "kernel.sem = 250 32000 100 128"
    "net.ipv4.ip_local_port_range = 9000 65500"
    "net.ipv4.tcp_retries2 = 3"
    "net.core.rmem_default = 262144"
    "net.core.rmem_max = 4194304"
    "net.core.wmem_default = 262144"
    "net.core.wmem_max = 1048576"
    "vm.swappiness = 0"
    "vm.dirty_background_ratio = 3"
    "vm.dirty_ratio = 80"
    "vm.dirty_expire_centisecs = 500"
    "vm.dirty_writeback_centisecs = 100"
    "vm.numa_stat = 0"
    "vm.overcommit_memory=0"
  )
  if [[ $(grep -E -c "# DMBegin" /etc/sysctl.conf) = 0 ]]; then
    printf "%s\n" "${params[@]}" | tee -a /etc/sysctl.conf >/dev/null
  fi
  sysctl -p
}

#==============================================================#
#                           配置系统资源                         #
#==============================================================#
function conf_limits() {
  local limits_file="/etc/security/limits.conf"
  local login_file="/etc/pam.d/login"
  local systemd_file="/etc/systemd/system.conf"

  log_print "配置系统资源" ""

  # 检查文件是否已经被修改过，如果已经修改过，则不再执行修改操作
  if grep -q "# DMBegin" "$limits_file" && grep -q "# DMBegin" "$login_file"; then
    echo "$limits_file和$login_file文件已经被修改过，无需再次修改。"
    sed -n '/# DMBegin/,/# DMEnd/p' "$limits_file" | grep -v '^#|^*#|^$'
    sed -n '/# DMBegin/,/# DMEnd/p' "$login_file" | grep -v '^#|^*#|^$'
    sed -n '/DefaultLimit/p' "$systemd_file" | grep -v '^#'
  # 检查文件是否存在，如果不存在，则不执行修改操作
  elif [[ ! -f "$limits_file" ]]; then
    color_printf red "$limits_file文件不存在，无法进行修改操作。"
  elif [[ ! -f "$login_file" ]]; then
    color_printf red "$login_file文件不存在，无法进行修改操作。"
  else
    # 备份文件
    bak_file "$limits_file"
    bak_file "$login_file"

    # 修改/etc/security/limits.conf文件
    if ! grep -q "# DMBegin" "$limits_file"; then
      echo "# DMBegin" >>"$limits_file"
      cat <<-'EOF' >>"$limits_file"
dmdba - nice   0
dmdba - as     unlimited
dmdba - fsize  unlimited
dmdba - nproc  131072
dmdba - nofile 131072
dmdba - core   unlimited
dmdba - data   unlimited
root  - nice   0
root  - as     unlimited
root  - fsize  unlimited
root  - nproc  131072
root  - nofile 131072
root  - core   unlimited
root  - data   unlimited
# DMEnd
EOF
    fi

    sed -n '/# DMBegin/,/# DMEnd/p' "$limits_file" | grep -v '^#|^*#|^$'
    echo

    # 修改/etc/pam.d/login文件
    if ! grep -q "# DMBegin" "$login_file"; then
      echo "# DMBegin" >>"$login_file"
      cat <<-'EOF' >>"$login_file"
session required pam_limits.so
session required /lib64/security/pam_limits.so
# DMEnd
EOF
    fi

    sed -n '/# DMBegin/,/# DMEnd/p' "$login_file" | grep -v '^#|^*#|^$'
    echo

    # 在CentOS7 / RHEL7系统中使用Systemd替代了之前的SysV，需要修改/etc/systemd/system.conf文件。
    if [[ $os_version -ne "6" && -f "$systemd_file" ]]; then
      # 备份文件
      bak_file "$systemd_file"
      # 修改文件
      if ! grep -q "DefaultLimitCORE=infinity" "$systemd_file"; then
        sed -i '/#DefaultLimitCORE=/ s/.*/DefaultLimitCORE=infinity/' "$systemd_file"
      fi
      if ! grep -q "DefaultLimitNOFILE=65536" "$systemd_file"; then
        sed -i '/#DefaultLimitNOFILE=/ s/.*/DefaultLimitNOFILE=65536/' "$systemd_file"
      fi
      if ! grep -q "DefaultLimitNPROC=10240" "$systemd_file"; then
        sed -i '/#DefaultLimitNPROC=/ s/.*/DefaultLimitNPROC=10240/' "$systemd_file"
      fi
      sed -n '/DefaultLimit/p' "$systemd_file" | grep -v '^#'
    fi
  fi
}

#==============================================================#
#                     Configure RemoveIPC                      #
#==============================================================#
function conf_ipc() {
  if [[ $os_version -ne "6" ]]; then
    log_print "禁用 RemoveIPC" ""

    # 检查文件是否已经被修改过
    if grep -q "^RemoveIPC=no" /etc/systemd/logind.conf; then
      echo "/etc/systemd/logind.conf文件已经被修改过，无需再次修改。"
      awk '/RemoveIPC/ {print}' /etc/systemd/logind.conf
    else
      # 检查文件是否存在
      if [[ ! -f "/etc/systemd/logind.conf" ]]; then
        color_printf red "/etc/systemd/logind.conf文件不存在，无法进行修改操作。"
        return 1
      fi

      # 备份文件
      bak_file /etc/systemd/logind.conf

      # 修改文件
      sed -i 's/#RemoveIPC=no/RemoveIPC=no/g' /etc/systemd/logind.conf

      # 重新加载daemon和重启systemd-logind服务
      systemctl daemon-reload
      systemctl restart systemd-logind

      # 显示修改后的文件内容
      awk '/#RemoveIPC/{print} !/#RemoveIPC/{print}' /etc/systemd/logind.conf | grep -v '^#' | sed '/^$/d'
    fi
  fi
}

#==============================================================#
#                          配置语言中文                          #
#==============================================================#
function conf_lang() {
  log_print "配置语言中文" ""
  # 检查文件是否已经被修改过，如果已经修改过，则不再执行修改操作
  if grep -q "export LANG=zh_CN.UTF-8" /etc/profile; then
    echo "/etc/profile文件已经被修改过，无需再次修改。"
    tail -n 1 /etc/profile
  # 检查文件是否存在，如果不存在，则不执行修改操作
  elif [[ ! -f "/etc/profile" ]]; then
    color_printf red "/etc/profile文件不存在，无法进行修改操作。"
  else
    # 配置操作系统支持的所有字符集
    if [[ $os_version -ne "6" ]]; then
      if locale -a | grep -q "zh_CN.utf8"; then
        bak_file /etc/locale.conf
        echo -e "LANG=zh_CN.utf8\nLC_CTYPE=zh_CN.utf8" >/etc/locale.conf
        grep </etc/locale.conf "zh_CN.utf8"
      else
        color_printf red "抱歉，当前系统没有中文环境：zh_CN.UTF-8，无法继续安装！"
        exit 99
      fi
    fi
    # 修改文件
    bak_file /etc/profile
    echo 'export LANG=zh_CN.UTF-8' >>/etc/profile
    # shellcheck source=/dev/null
    source /etc/profile
    awk '/export LANG/{print}' /etc/profile | grep -v '^#|^*#|^$'
  fi
}

#==============================================================#
#                          配置drop_cache脚本                   #
#==============================================================#
function drop_cache() {
  # 配置缓存清除脚本，使用cron job在指定时间清除缓存

  log_print "配置cache脚本" "" # 打印日志，记录函数执行进度

  local CRONTAB_FILE="/var/spool/cron/root"
  local SCRIPT_FILE="/root/drop_cache.sh"

  # 备份现有的crontab文件
  if [[ -e $CRONTAB_FILE ]]; then
    bak_file "$CRONTAB_FILE"
  fi

  # 创建缓存清除脚本
  cat <<-'EOF' >"$SCRIPT_FILE"
#!/bin/bash
sync
echo 1 > /proc/sys/vm/drop_caches
EOF

  chmod 775 "$SCRIPT_FILE"
  # 添加cron job到crontab文件
  if ! crontab -l | grep -q "$SCRIPT_FILE"; then
    echo "# DMBegin" >>"$CRONTAB_FILE"
    echo "* 6 * * * $SCRIPT_FILE" >>"$CRONTAB_FILE"
  fi

  # 打印缓存清除脚本和当前的crontab配置
  cat "$SCRIPT_FILE" && crontab -l
}
#==============================================================#
#                       配置用户环境变量                         #
#==============================================================#
# 此函数用于配置用户环境变量
function conf_profile() {
  log_print "配置用户环境变量" ""

  # 数据库实例路径
  local data_path
  if [[ $dm_install_mode = "dsc" ]]; then
    data_path="$data_dir"/"$dsc_db_name"
  else
    data_path="$data_dir"/"$db_name"
  fi

  # 更新 root 用户的 bash 配置文件
  bak_file /root/.bash_profile
  cat <<-'EOF' >>/root/.bash_profile
# DMBegin
alias sd='su - dmdba'
export PS1="[\u@\h:\w]\$ "
EOF

  # 更新 dmdba 用户的 bash 配置文件
  bak_file /home/dmdba/.bash_profile
  cat <<-EOF >>/home/dmdba/.bash_profile
# DMBegin
export MALLOC_ARENA_MAX=4
export DM_HOME="$env_dm_home"
export LD_LIBRARY_PATH="\$LD_LIBRARY_PATH:\$DM_HOME/bin"
export PATH="\$PATH:\$DM_HOME/bin:\$DM_HOME/tool"
export PS1="[\u@\h:\w]\$ "
alias dmbin="cd \$DM_HOME/bin"
alias dmlog="cd \$DM_HOME/log"
alias dmdata="cd $data_path"
alias ds="disql -L SYSDBA/'\"${sysdba_pwd//\$/\\\$}\"':$port_num"
alias dsql="disql -L -S SYSDBA/'\"${sysdba_pwd//\$/\\\$}\"':$port_num"
alias dssql="disql -L -S SYSDBA/'\"${sysdba_pwd//\$/\\\$}\"':$port_num"
EOF

  # 输出更新后的 dmdba 用户的 bash 配置文件内容
  grep </home/dmdba/.bash_profile -E -v '^#|^$'

  # 将 DM_HOME/bin 添加到系统库搜索路径并更新缓存
  echo "$env_dm_home/bin" >/etc/ld.so.conf.d/dm.conf
  ldconfig
}

#==============================================================#
#                         挂载达梦安装镜像                       #
#==============================================================#
# 此函数用于挂载 DMISO 文件
function mount_iso() {
  log_print "挂载DMISO" ""

  # 创建或卸载 /dmiso 目录
  if [[ -d /dmiso ]]; then
    umount -f /dmiso
  else
    mkdir /dmiso
  fi

  # 创建或卸载 /run/media/root/dameng 目录
  if [[ -d /run/media/root/dameng ]]; then
    umount -f /run/media/root/dameng
  fi

  # 将 ISO 文件挂载到 /dmiso 目录
  mount -t auto -o ro,loop "$software_dir/$dm_iso_name" /dmiso

  # 列出 /dmiso 目录下的内容
  ls -lrth /dmiso
}

#==============================================================#
#                       安装达梦数据库软件                       #
#==============================================================#
function install_dmdb() {
  log_print "开始安装达梦数据库软件" ""
  # 检查安装文件是否存在
  if [[ ! -f "$software_dir/DMInstall.bin" && (! $dm_iso_name || ! -f /dmiso/DMInstall.bin) ]]; then
    color_printf red "找不到达梦数据库安装程序 DMInstall.bin"
    exit 1
  fi
  # 如果存在，备份文件
  if [ -f /etc/dm_svc.conf ]; then
    mv /etc/dm_svc.conf /etc/dm_svc.conf.bak # 重命名原始文件
  fi

  # 生成安装配置文件 auto_install.xml
  local lang=zh
  if [[ $LANG = "zh_CN.UTF-8" ]]; then
    lang=zh
  elif [[ $LANG = "en_US.UTF-8" ]]; then
    lang=en
  fi
  cat <<-EOF >"$software_dir"/auto_install.xml
<?xml version="1.0"?>
<DATABASE>
    <LANGUAGE>$lang</LANGUAGE>
    <TIME_ZONE>+08:00</TIME_ZONE>
    <KEY>$key_path</KEY>
    <INSTALL_TYPE>0</INSTALL_TYPE>
    <INSTALL_PATH>$env_dm_home</INSTALL_PATH>
    <INIT_DB>N</INIT_DB>
</DATABASE>
EOF

  # 安装达梦数据库软件
  export DM_INSTALL_TMPDIR=$software_dir
  ulimit -n 65536
  if [[ $dm_iso_name && -e /dmiso/DMInstall.bin ]]; then
    /dmiso/DMInstall.bin -q "$software_dir"/auto_install.xml
  elif [[ -e $software_dir/DMInstall.bin ]]; then
    chmod +x "$software_dir"/DMInstall.bin
    "$software_dir"/DMInstall.bin -q "$software_dir"/auto_install.xml
  else
    color_printf red "找不到达梦数据库安装程序 DMInstall.bin"
    exit 1
  fi
  if [ $? -ne 0 ]; then
    color_printf red "达梦数据库软件安装失败"
    if [ -f /etc/dm_svc.conf.bak ]; then
      cp -pf /etc/dm_svc.conf.bak /etc/dm_svc.conf # 将备份文件还原为原始文件
    fi
    exit 1
  fi

  # 配置 glogin.sql
  cat <<-'EOF' >"$env_dm_home"/bin/disql_conf/glogin.sql
-- 显示密钥过期时间
column expired_date new_value _edate
select to_char(expired_date,'yyyy-mm-dd') expired_date from v$license;
host echo "密钥过期时间：&_edate"
-- 设置dbms_output输出缓冲区大小
set serveroutput on size 1000000
-- 设置输出格式
set long 200
set linesize 500
set pagesize 5000
-- 去除重定向输出每行拖尾空格
set trimspool on
-- 去除行号显示
set lineshow off
-- 显示当前连接信息
col name new_value _dname
select name from v$database;
col port_num new_value _port
select para_value port_num from v$dm_ini where para_name='PORT_NUM';
set SQLPROMPT "_USER'@'_dname':'_port SQL> "
-- 显示当前时间
set time on
EOF

  # 分布式模式需要添加动态链接库软链接
  if [[ $dm_install_mode = "dsc" ]]; then
    if [[ $(find /lib64/* -maxdepth 1 -name "libdm*.so" | wc -l) -le 6 ]]; then
      ln -s "$env_dm_home"/bin/*.so /lib64
    fi
  fi
}

function init_dmdb() {
  log_print "初始化达梦数据库" ""

  local dm_user="dmdba"
  local dm_init_cmd="$env_dm_home/bin/dminit"

  # 检查参数是否有效
  if [[ -z "$data_dir" || -z "$db_name" || -z "$instance_name" || -z "$port_num" ]]; then
    color_printf red "错误：参数不能为空"
    return 1
  fi

  # 执行 dminit 命令

  # 执行命令
  su - "$dm_user" -c "$dm_init_cmd \
    PATH=$data_dir \
    EXTENT_SIZE=$extent_size \
    PAGE_SIZE=$page_size \
    CASE_SENSITIVE=$case_sensitive \
    CHARSET=$charset \
    LENGTH_IN_CHAR=$length_in_char \
    LOG_SIZE=$log_size \
    DB_NAME=$db_name \
    INSTANCE_NAME=$instance_name \
    PORT_NUM=$port_num"

  if [ $? -ne 0 ]; then
    color_printf red "错误：执行 dminit 命令失败"
    return 1
  fi
}

#==============================================================#
#                       达梦数据库异步追踪日志                   #
#==============================================================#
function dm_sqllog() {
  # 定义sqllog路径
  local db_dir
  # 定义sqllog.ini
  create_ini_file() {
    local db_dir="$1"
    su - dmdba <<-EOSQLLOG
    cat <<-EOF >"${db_dir}.ini"
BUF_TOTAL_SIZE          = 10240         #SQLs Log Buffer Total Size(K)(1024~1024000)
BUF_SIZE                = 1024          #SQLs Log Buffer Size(K)(50~409600)
BUF_KEEP_CNT            = 6             #SQLs Log buffer keeped count(1~100)

[SLOG_ALL]
FILE_NUM      = 100
SWITCH_MODE   = 2
SWITCH_LIMIT  = 256
ASYNC_FLUSH   = 1
FILE_PATH     = $db_dir/
SQL_TRACE_MASK = 2:3:22:25:28
MIN_EXEC_TIME = 100
EOF
EOSQLLOG
  }
  if [[ $dm_install_mode = "dsc" ]]; then
    # 如果安装模式为"dsc"
    for ((x = 0; x < ${#wc_pubip_arr[@]}; x++)); do
      # 针对每个IP地址，创建相应的SQL日志目录
      db_dir="$data_dir/$dsc_db_name/$(lower "$dsc_ep_name")${x}_config/sqllog"
      mkdir -p "$db_dir"                #创建目录及子目录
      chown -R dmdba.dinstall "$db_dir" # 给目录赋予适当的权限
      create_ini_file "$db_dir"
    done
  else
    #如果不是"dsc"安装模式，则只需创建一个SQL日志目录
    db_dir="$data_dir/$db_name/sqllog"
    mkdir -p "$db_dir"
    chown -R dmdba.dinstall "$db_dir"
    create_ini_file "$db_dir"
  fi
}

#==============================================================#
#                           注册实例服务                        #
#==============================================================#
function install_dmdb_svc() {
  log_print "注册数据库服务" ""
  readonly SERVICE_TYPE="dmserver"
  readonly CONFIG_FILE="$data_dir/$db_name/dm.ini"
  local service_file="/usr/lib/systemd/system/DmService${db_name}.service"

  if [[ $os_version != "6" ]]; then
    rm -f "$service_file"
  fi

  "$env_dm_home"/script/root/dm_service_installer.sh -t "$SERVICE_TYPE" -dm_ini "$CONFIG_FILE" -p "$db_name"

  if [[ $os_version = "6" ]]; then
    ln -sf /etc/rc.d/init.d/DmService"$db_name" "$env_dm_home"/bin/DmService"$db_name"
    chmod 755 "$env_dm_home"/bin/DmService"$db_name"
  fi
}

#==============================================================#
#                           启停数据库                          #
#==============================================================#
function start_dmdb() {
  readonly DM_SERVER_CMD="$env_dm_home/bin/dmserver"
  readonly DM_SERVICE_CMD="su - dmdba -c \"DmService$db_name start\""
  dmdb_info_sql=$(
    cat <<-'EOF'
set echo off
set timing off
set feedback off
set lineshow off
SELECT '实例名' 数据库参数项,
        instance_name 数据库参数值
  FROM v$instance
UNION ALL
SELECT svr_version,
        build_version "数据库版本"
  FROM v$instance
UNION ALL 
SELECT '簇大小',CAST(sf_get_extent_size() AS VARCHAR) 
UNION ALL
SELECT '页大小',CAST(page() / 1024 AS VARCHAR) 
UNION ALL 
SELECT '大小写敏感',CAST(sf_get_case_sensitive_flag() AS VARCHAR)
UNION ALL 
SELECT '字符集',CAST(sf_get_unicode_flag() AS VARCHAR)
UNION ALL 
SELECT 'varchar是否以字符为单位',CAST(sf_get_length_in_char() AS VARCHAR);
exit;
EOF
  )
  readonly dmdb_info_sql
  conf_dbinfo_file="/home/dmdba/scripts/dmdb_info.sql"
  echo "$dmdb_info_sql" >"$conf_dbinfo_file"
  chown dmdba.dinstall "$conf_dbinfo_file"

  ## 前台静默启停
  su - dmdba -c "$DM_SERVER_CMD $data_dir/$db_name/dm.ini <<-EOF
exit
EOF
" >/dev/null 2>&1
  echo
  ## 启动数据库
  eval "$DM_SERVICE_CMD"
  echo
  su - dmdba -c "DmService$db_name status"
  ## 查看数据库信息
  log_print "查询数据库基础参数信息" ""
  su - dmdba -c "disql -S SYSDBA/SYSDBA:$port_num '\`$conf_dbinfo_file'"
}

#==============================================================#
#                       达梦数据库基础配置                       #
#==============================================================#
##配置单机归档
function si_dmarch() {
  # 启用归档日志
  if [[ $enable_arch = "Y" ]]; then
    # 如果归档目录不存在，则创建它
    if [[ ! -d $arch_dir ]]; then
      mkdir -p "$arch_dir"
      chown -R dmdba:dinstall "$arch_dir"
    fi

    # 为特定数据库实例创建归档目录
    db_arch_dir="$arch_dir/$db_name"
    if [[ ! -d $db_arch_dir ]]; then
      mkdir -p "$db_arch_dir"
      chown -R dmdba:dinstall "$db_arch_dir"
    fi

    # 生成配置归档日志的SQL脚本
    conf_arch_sql=$(
      cat <<-EOF
set echo off
set timing off
set feedback off
set lineshow off
ALTER DATABASE MOUNT;
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE ADD ARCHIVELOG 'DEST=$db_arch_dir, TYPE=LOCAL, FILE_SIZE=$log_size, SPACE_LIMIT=$space_limit';
ALTER DATABASE OPEN;
select arch_mode from v\$database;
SELECT arch_name,
        arch_dest,
        arch_file_size,
        arch_space_limit
  FROM v\$dm_arch_ini;
exit;
EOF
    )

    # 将SQL脚本写入文件
    conf_arch_file="/home/dmdba/scripts/conf_arch.sql"
    echo "$conf_arch_sql" >"$conf_arch_file"
    chown dmdba.dinstall "$conf_arch_file"
  fi
}

function dsc_dmarch() {

  local dsc_dmarch_file="/home/dmdba/scripts/conf_arch.sql"

  # 添加归档日志目的地到数据库
  add_archive_log_destination() {
    local dest_type=$1         # 归档日志目的地类型（本地或远程）
    local dest_path=$2         # 归档日志目的地路径
    local file_size=$3         # 归档日志文件大小
    local space_limit_value=$4 # 归档日志文件空间限制
    local incoming_path=$5     # 可选的远程归档日志目的地的传入路径

    su - dmdba <<-DMARCH
      cat <<-EOF>>"$dsc_dmarch_file"
ALTER DATABASE ADD ARCHIVELOG 'DEST = $dest_path, TYPE = $dest_type, FILE_SIZE = $file_size, SPACE_LIMIT = $space_limit_value${incoming_path:+, INCOMING_PATH = $incoming_path}';
EOF
DMARCH
  }

  if [[ $arch_disk = "" && $is_local_arch = "N" ]]; then
    space_limit_value=$space_limit
  else
    space_limit_value=$(echo "$((space_limit - 10240))/${#wc_pubip_arr[@]}" | bc)
  fi

  lastIndex=$((${#asm_disk_arr[@]} - 1))
  su - dmdba <<-DMARCH
    cat <<-EOF>>"$dsc_dmarch_file"
ALTER DATABASE MOUNT; 
EOF
DMARCH

  if [[ -z $arch_disk ]]; then
    if [[ $is_local_arch = "N" ]]; then
      add_archive_log_destination "local" "+$(upper "${asm_disk_arr[$lastIndex]}")/dmarch/$dsc_db_name/${dsc_ep_name}$((node_num - 1))" "$log_size" "$space_limit_value"

      for ((x = 1; x <= ${#wc_pubip_arr[@]}; x++)); do
        if [[ $x != "$node_num" ]]; then
          add_archive_log_destination "REMOTE" "$(upper "${dsc_ep_name}")$((x - 1))" "$log_size"" $space_limit_value" "+$(upper "${asm_disk_arr[$lastIndex]}")/dmarch/$dsc_db_name/${dsc_ep_name}$((x - 1))"
        fi
      done
    else
      add_archive_log_destination "local" "$arch_dir/dmarch/$dsc_db_name/${dsc_ep_name}$((node_num - 1))" "$log_size"" $space_limit_value"

      for ((x = 1; x <= ${#wc_pubip_arr[@]}; x++)); do
        if [[ $x != "$node_num" ]]; then
          add_archive_log_destination "REMOTE" "$(upper "${dsc_ep_name}")$((x - 1))" "$log_size" "$space_limit_value" "$arch_dir/dmarch/$dsc_db_name/${dsc_ep_name}$((x - 1))"
        fi
      done
    fi
  else
    if [[ $is_local_arch = "N" ]]; then
      add_archive_log_destination "local" "+$(upper "${asm_disk_arr[$((lastIndex - 1))]}")/dmarch/$dsc_db_name/${dsc_ep_name}$((node_num - 1))" "$log_size" "$space_limit_value"

      for ((x = 1; x <= ${#wc_pubip_arr[@]}; x++)); do
        if [[ $x != "$node_num" ]]; then
          add_archive_log_destination "REMOTE" "$(upper "${dsc_ep_name}")$((x - 1))" "$log_size" "$space_limit_value" "+$(upper "${asm_disk_arr[$((lastIndex - 1))]}")/dmarch/$dsc_db_name/${dsc_ep_name}$((x - 1))"
        fi
      done
    else
      add_archive_log_destination "local" "+$(upper "${asm_disk_arr[$((lastIndex - 1))]}")/dmarch/$dsc_db_name/${dsc_ep_name}$((node_num - 1))" "$log_size" "$space_limit_value"

      for ((x = 1; x <= ${#wc_pubip_arr[@]}; x++)); do
        if [[ $x != "$node_num" ]]; then
          add_archive_log_destination "REMOTE" "$(upper "${dsc_ep_name}")$((x - 1))" "$log_size" "$space_limit_value" "+$(upper "${asm_disk_arr[$((lastIndex - 1))]}")/dmarch/$dsc_db_name/${dsc_ep_name}$((x - 1))"
        fi
      done
      add_archive_log_destination "local" "$arch_dir/dmarch/$dsc_db_name/${dsc_ep_name}$((node_num - 1))" "$log_size" "$space_limit_value"
    fi
  fi

  su - dmdba <<-DMARCH
    cat <<-EOF>>$dsc_dmarch_file
ALTER DATABASE ARCHIVELOG;
ALTER DATABASE OPEN;
exit;
EOF
DMARCH

  chown dmdba.dinstall "$dsc_dmarch_file"
}

#==============================================================#
#                       达梦数据库备份                         #
#==============================================================#
function dm_bak() {
  # 仅创建实例时，判断备份路径是否存在，没有则创建
  if [[ $only_init_db = "Y" ]]; then
    if [[ ! -d $backup_dir ]]; then
      mkdir "$backup_dir"
      chown -R dmdba:dinstall "$backup_dir"
    fi
  fi
  ## 创建实例备份路径
  [[ ! -d $backup_dir/$1 ]] && mkdir -p "$backup_dir"/"$1"
  chown -R dmdba:dinstall "$backup_dir"/"$1"
  cat <<-EOF >/home/dmdba/scripts/conf_fullbackup.sql
set echo off
set timing off
set feedback off
set lineshow off
SP_INIT_JOB_SYS(1);
call SP_CREATE_JOB('job_bak',1,0,'',0,0,'',0,'每天全量备份，并删除30天之前的备份。');
call SP_JOB_CONFIG_START('job_bak');
call SP_ADD_JOB_STEP('job_bak', 'job_bak_db', 6, '01000000$backup_dir/$1', 3, 1, 0, 0, NULL, 0);
call SP_ADD_JOB_STEP('job_bak', 'job_bak_del', 0, 'CALL SF_BAKSET_BACKUP_DIR_ADD(''DISK'',''$backup_dir/$1'');
CALL SP_DB_BAKSET_REMOVE_BATCH(''DISK'',SYSDATE()-30);', 1, 1, 0, 0, NULL, 0);
call SP_ADD_JOB_SCHEDULE('job_bak', 'job_bak', 1, 1, 1, 0, 0, '00:30:00', NULL, '2020-01-01 00:00:00', NULL, '');
call SP_JOB_CONFIG_COMMIT('job_bak');
SELECT NAME,
       describe
  FROM sysjob.sysjobs;
SELECT top 1 NAME,
       command
  FROM "SYSJOB"."SYSJOBSTEPS";
exit;
EOF

  cat <<-EOF >/home/dmdba/scripts/conf_incrbackup.sql
set echo off
set timing off
set feedback off
set lineshow off
SP_INIT_JOB_SYS(1);
call SP_CREATE_JOB('bak_full',1,0,'',0,0,'',0,'周六全量备份，并删除30天之前的备份。');
call SP_JOB_CONFIG_START('bak_full');
call SP_JOB_SET_EP_SEQNO('bak_full', 0);
call SP_ADD_JOB_STEP('bak_full', 'bak_full', 6, '01000000$backup_dir/$1', 3, 1, 0, 0, NULL, 0);
call SP_ADD_JOB_STEP('bak_full', 'bak_clear', 0, 'CALL SF_BAKSET_BACKUP_DIR_ADD(''DISK'',''$backup_dir/$1'');
CALL SP_DB_BAKSET_REMOVE_BATCH(''DISK'',SYSDATE()-30);', 1, 1, 0, 0, NULL, 0);
call SP_ADD_JOB_SCHEDULE('bak_full', 'bak_full', 1, 2, 1, 64, 0, '00:30:00', NULL, '2020-01-01 00:00:00', NULL, '');
call SP_JOB_CONFIG_COMMIT('bak_full');
call SP_CREATE_JOB('bak_inc',1,0,'',0,0,'',0,'周日到周五做增量备份,如果失败,清除8天前备份,做全量备份');
call SP_JOB_CONFIG_START('bak_inc');
call SP_ADD_JOB_STEP('bak_inc', 'bak_inc', 6, '11000000$backup_dir/$1|$backup_dir/$1', 1, 3, 2, 6, NULL, 0);
call SP_ADD_JOB_STEP('bak_inc', 'switch_bak_full', 6, '01000000$backup_dir/$1', 1, 1, 0, 0, NULL, 0);
call SP_ADD_JOB_STEP('bak_inc', 'bak_clear', 0, 'SF_BAKSET_BACKUP_DIR_ADD(''DISK'',''$backup_dir/$1'');
CALL SP_DB_BAKSET_REMOVE_BATCH(''DISK'',SYSDATE()-8);', 3, 1, 0, 0, NULL, 0);
call SP_ADD_JOB_SCHEDULE('bak_inc', 'bak_inc', 1, 2, 1, 63, 0, '00:30:00', NULL, '2020-01-01 00:00:00', NULL, '');
call SP_JOB_CONFIG_COMMIT('bak_inc');
SELECT NAME,
       describe
  FROM sysjob.sysjobs;
SELECT top 1 NAME,
       command
  FROM "SYSJOB"."SYSJOBSTEPS";
exit;
EOF
  chown dmdba.dinstall /home/dmdba/scripts/conf_fullbackup.sql
  chown dmdba.dinstall /home/dmdba/scripts/conf_incrbackup.sql
  ls -lh /home/dmdba/scripts/conf_fullbackup.sql && ls -lh /home/dmdba/scripts/conf_incrbackup.sql

}

function conf_arch_bak_user() {
  # 检查必要参数是否存在
  if [[ -z "$dm_install_mode" ]] || [[ -z "$sysdba_pwd" ]]; then
    color_printf red "必要参数 dm_install_mode 或 sysdba_pwd 不存在"
    return 1
  fi

  # 创建归档和备份脚本
  log_print "创建归档和备份脚本" ""
  if [[ $dm_install_mode = "dsc" ]]; then
    dsc_dmarch
  else
    si_dmarch
  fi

  # 输出归档脚本路径
  color_printf blue "创建数据库归档脚本："
  echo
  ls -lh /home/dmdba/scripts/conf_arch.sql
  echo

  # 创建备份脚本
  color_printf blue "创建数据库备份脚本："
  echo
  if [[ $dm_install_mode = "dsc" ]]; then
    dm_bak "$dsc_db_name"
  else
    dm_bak "$db_name"
  fi

  # 创建 DMDBA 用户
  echo
  color_printf blue "创建 DMDBA 用户脚本，密码 $sysdba_pwd ："
  cat <<-EOF >/home/dmdba/scripts/ct_dbuser.sql
set timing off
set lineshow off
set feedback off
create user "DMDBA" identified by "$sysdba_pwd" password_policy 0;
grant "DBA","PUBLIC","RESOURCE","SOI","VTI" to "DMDBA" with admin option;
-- 修改 SYSDBA 密码
alter user "SYSDBA" identified by "$sysdba_pwd" password_policy 0;
-- 密码策略调整
ALTER USER "SYSDBA" LIMIT FAILED_LOGIN_ATTEMPS UNLIMITED;
ALTER USER "SYSDBA" LIMIT PASSWORD_LIFE_TIME UNLIMITED;
ALTER USER "SYSDBA" LIMIT PASSWORD_LOCK_TIME UNLIMITED;
ALTER USER "DMDBA" LIMIT FAILED_LOGIN_ATTEMPS UNLIMITED;
ALTER USER "DMDBA" LIMIT PASSWORD_LIFE_TIME UNLIMITED;
ALTER USER "DMDBA" LIMIT PASSWORD_LOCK_TIME UNLIMITED;
select username from dba_users where account_status='OPEN';
exit;
EOF

  # 修改脚本所有权
  chown dmdba.dinstall /home/dmdba/scripts/ct_dbuser.sql

  # 输出 DMDBA 用户脚本路径
  echo
  ls -lh /home/dmdba/scripts/ct_dbuser.sql

  # 检查命令执行是否成功，如果失败则输出错误信息并退出函数
  if [[ $? -ne 0 ]]; then
    color_printf red "创建归档和备份脚本以及 DMDBA 用户脚本失败，请检查日志"
    return 1
  fi
}

#==============================================================#
#                       达梦数据库优化配置                       #
#==============================================================#
function optimize_dmini() {
  log_print "创建达梦数据库优化脚本" ""
  local conf_script="/home/dmdba/scripts/conf_para.sql"
  local query_script="/home/dmdba/scripts/query_dm.sql"

  # 创建数据库参数配置脚本
  color_printf blue "创建数据库参数配置脚本:"
  echo
  cat <<-EOF >"${conf_script}"
SET echo OFF
SET serveroutput ON
SET timing OFF
SET feedback OFF
SET lineshow OFF
declare
    exec_mode            int:= 0;            --0表示直接执行脚本修改参数，1表示不直接修改参数，打印设置参数的语句，设置为1后，必须调整v_mem_mb和v_cpus
    mem_per              int:= $mem_percent; --默认所有的内存归达梦数据库使用，如实际不能100%可用，可以调整此参数
    v_mem_mb             int:= 16000;        --exec_mode为1时请自行根据机器实际内存调整此参数
    v_cpus               int:= 8;            --exec_mode为1时请自行根据机器实际CPU核数调整此参数
    sort_mode            int:= $sort_mode;   --并发量较高的OLTP类型系统此参数设置为0，并发量不高的一般业务系统和OLAP类的系统此参数设置为1
    pk_cluster_mode      int:= 1;            --是否使用聚集主键：性能要求高且大字段较少的业务场景建议设置为1，大字段多的场景设置为0
    tname                varchar(100);
    MEMORY_POOL          int;
    MEMORY_N_POOLS       int;
    MEMORY_TARGET        int;
    BUFFER               INT;
    MAX_BUFFER           INT;
    RECYCLE              int;
    CACHE_POOL_SIZE      int;
    BUFFER_POOLS         int;
    RECYCLE_POOLS        int;
    SORT_BUF_SIZE        int;
    SORT_BUF_GLOBAL_SIZE INT;
    DICT_BUF_SIZE        INT;
    HJ_BUF_SIZE          INT;
    HAGR_BUF_SIZE        INT;
    HJ_BUF_GLOBAL_SIZE   INT;
    HAGR_BUF_GLOBAL_SIZE INT;
    SORT_FLAG            INT;
    SORT_BLK_SIZE        INT;
    RLOG_POOL_SIZE       INT;
    TASK_THREADS         INT;
    IO_THR_GROUPS        INT;
    FAST_POOL_PAGES      INT :=3000;
    FAST_ROLL_PAGES      INT :=1000;
    CNT                  INT;
begin
    CNT :=0;
    if exec_mode=0 then
        SELECT TOP 1 N_CPU, 
               TOTAL_PHY_SIZE/1024/1024 
          INTO 
               v_cpus, 
               v_mem_mb 
          FROM V\$SYSTEMINFO;
     
    end if;
    v_mem_mb := v_mem_mb * (mem_per/100.0);
    v_mem_mb     =round(v_mem_mb,-3);
    IF v_mem_mb <= 999 THEN
        goto return_2000;
    END IF;
    IF v_mem_mb > 512000 THEN
        v_mem_mb :=v_mem_mb*0.8;
    END IF;
    MEMORY_TARGET=round(cast(v_mem_mb * 0.12 as int),-3);
    TASK_THREADS  :=4;
    IO_THR_GROUPS :=4;
    IF v_cpus < 8 THEN
        TASK_THREADS  :=4;
        IO_THR_GROUPS :=2;
    END IF;
    IF v_cpus >= 64 THEN
        v_cpus        := 64;
        TASK_THREADS  :=16;
        IO_THR_GROUPS :=8;
    END IF;
    BUFFER  := round(cast(v_mem_mb * 0.4 as int),-3);
    RECYCLE :=cast(v_mem_mb * 0.04 as int);
    IF v_mem_mb < 70000 THEN
        with t as
             ( select rownum rn 
                from dual 
                     connect by level <= 100
             ) 
             ,
             t1 as
             ( select * from t where rn > 1 
             minus
             select ta.rn * tb.rn
               from t ta,
                    t tb
              where ta.rn <= tb.rn
                and ta.rn > 1
                and tb.rn > 1
             )
          select top 1 rn 
            into 
                 BUFFER_POOLS 
            from t1 
           where rn > v_mem_mb/800 
        order by 1;
    
    ELSE
        BUFFER_POOLS := 101;
    END IF;
    --修改内存池
    IF v_mem_mb   >= 16000 THEN
        IF v_mem_mb= 16000 THEN
            MEMORY_POOL          := 1500;
            SORT_BUF_GLOBAL_SIZE := 1000;
            MEMORY_N_POOLS       := 3;
            CACHE_POOL_SIZE      := 512;
        ELSE
            MEMORY_POOL          := 2000;
            SORT_BUF_GLOBAL_SIZE := 2000;
            MEMORY_N_POOLS       := 11;
            CACHE_POOL_SIZE      := 1024;
        END IF;
        FAST_POOL_PAGES :=9999;
        SORT_FLAG    = 0;
        SORT_BLK_SIZE=1;
        SORT_BUF_SIZE        := 10;
        RLOG_POOL_SIZE       := 1024;
        HJ_BUF_GLOBAL_SIZE   := LEAST(cast(v_mem_mb * 0.0625 as int),10000);
        HAGR_BUF_GLOBAL_SIZE := LEAST(cast(v_mem_mb * 0.0625 as int),10000);
        HJ_BUF_SIZE          :=250;
        HAGR_BUF_SIZE        :=250;
        IF v_mem_mb >= 64000 THEN
            FAST_POOL_PAGES :=99999;
            FAST_ROLL_PAGES :=9999;
            BUFFER          :=BUFFER-3000;
            CACHE_POOL_SIZE := 2048;
            RLOG_POOL_SIZE  := 2048;
            SORT_FLAG           = 1;
            SORT_BLK_SIZE       =1;
            SORT_BUF_SIZE       =50;
            SORT_BUF_GLOBAL_SIZE= cast(v_mem_mb * 0.02 as    int);
            HJ_BUF_GLOBAL_SIZE   := cast(v_mem_mb * 0.15625 as int);
            HAGR_BUF_GLOBAL_SIZE := cast(v_mem_mb * 0.04 as    int);
            HJ_BUF_SIZE          :=512;
            HAGR_BUF_SIZE        :=512;
            MEMORY_N_POOLS       := 59;
        END IF;
        DICT_BUF_SIZE        := 50;
        HJ_BUF_GLOBAL_SIZE   :=round(HJ_BUF_GLOBAL_SIZE,-3);
        HAGR_BUF_GLOBAL_SIZE :=round(HAGR_BUF_GLOBAL_SIZE,-3);
        SORT_BUF_GLOBAL_SIZE :=round(SORT_BUF_GLOBAL_SIZE,-3);
        RECYCLE              :=round(RECYCLE,-3);
    ELSE
        MEMORY_POOL          :=GREAT(cast(v_mem_mb * 0.0625 as int),100);
        MEMORY_POOL          :=round(MEMORY_POOL,-2);
        MEMORY_N_POOLS       := 2;
        CACHE_POOL_SIZE      := 200;
        RLOG_POOL_SIZE       := 256;
        SORT_BUF_SIZE        := 10;
        SORT_BUF_GLOBAL_SIZE := 500;
        DICT_BUF_SIZE        := 50;
        SORT_FLAG    = 0;
        SORT_BLK_SIZE=1;
        HJ_BUF_GLOBAL_SIZE   := GREAT(cast(v_mem_mb * 0.0625 as  int),500);
        HAGR_BUF_GLOBAL_SIZE := GREAT(cast(v_mem_mb * 0.0625 as  int),500);
        HJ_BUF_SIZE          := GREAT(cast(v_mem_mb * 0.00625 as int),50);
        HAGR_BUF_SIZE        :=GREAT(cast(v_mem_mb * 0.00625 as  int),50);
    END IF;
    --设置根据RECYCLE情况RECYCLE_POOLS参数
    with t as
         ( select rownum rn 
            from dual 
                 connect by level <= 100
         ) 
         ,
         t1 as
         ( select * from t where rn > 1 
         minus
         select ta.rn * tb.rn
           from t ta,
                t tb
          where ta.rn <= tb.rn
            and ta.rn > 1
            and tb.rn > 1
         )
      select top 1 rn 
        into 
             RECYCLE_POOLS 
        from t1 
       where rn <= RECYCLE*1024/3000/(page()/1024) 
    order by 1 desc;
    
    tname :='BAK_DMINI_' || to_char(sysdate,'yymmdd');
    execute IMMEDIATE 'select count(*) from USER_ALL_TABLES where table_name= ?' into CNT using tname;
    if exists(select 1 from V\$INSTANCE where MODE$ in ('NORMAL','PRIMARY')) then
        IF CNT=0 THEN
            execute IMMEDIATE 'CREATE TABLE BAK_DMINI_' || to_char(sysdate,'yymmdd') || ' as select *,sysdate uptime from v\$dm_ini';
        ELSE
            execute IMMEDIATE 'INSERT INTO  BAK_DMINI_' || to_char(sysdate,'yymmdd') || ' select *,sysdate uptime from v\$dm_ini';
        END IF;
    end if;
    --如果sort_mode设置为0，采用旧的排序模式
    if sort_mode  =0 then
        SORT_FLAG = 0;
        SORT_BUF_SIZE := 2;
    end if;
    MAX_BUFFER := BUFFER;
    IF exec_mode=0 THEN
        --修改cpu相关参数
        SP_SET_PARA_VALUE(2,'WORKER_THREADS',v_cpus);
        SP_SET_PARA_VALUE(2,'TASK_THREADS',TASK_THREADS);
        SP_SET_PARA_VALUE(2,'IO_THR_GROUPS',IO_THR_GROUPS);
        --修改内存池相关参数
        SP_SET_PARA_VALUE(2,'MAX_OS_MEMORY', mem_per);
        SP_SET_PARA_VALUE(2,'MEMORY_POOL', MEMORY_POOL);
        SP_SET_PARA_VALUE(2,'MEMORY_N_POOLS', MEMORY_N_POOLS);
        SP_SET_PARA_VALUE(2,'MEMORY_TARGET', MEMORY_TARGET);
        --修改内存检测参数为1 	
        SP_SET_PARA_VALUE(2,'MEMORY_MAGIC_CHECK', 1);
        --修改缓冲区相关参数
        SP_SET_PARA_VALUE(2,'BUFFER', BUFFER);
        --新版本已去掉MAX_BUFFER参数，如果存在就修改
        IF EXISTS (SELECT * FROM V\$DM_INI WHERE PARA_NAME='MAX_BUFFER') THEN
            SP_SET_PARA_VALUE(2,'MAX_BUFFER', MAX_BUFFER);
        END IF;
        SP_SET_PARA_VALUE(2,'BUFFER_POOLS', BUFFER_POOLS);
        SP_SET_PARA_VALUE(2,'RECYCLE', RECYCLE);
        SP_SET_PARA_VALUE(2,'RECYCLE_POOLS', RECYCLE_POOLS);
        --修改fast_pool相关参数，如果是dsc环境，适当放小，以免影响启动速度
        IF EXISTS (SELECT 1 FROM v\$instance WHERE dsc_role <> 'NULL') THEN
            SP_SET_PARA_VALUE(2,'FAST_POOL_PAGES', 10000);
            SP_SET_PARA_VALUE(2,'FAST_ROLL_PAGES', 3000);
        ELSE
            SP_SET_PARA_VALUE(2,'FAST_POOL_PAGES', FAST_POOL_PAGES);
            SP_SET_PARA_VALUE(2,'FAST_ROLL_PAGES', FAST_ROLL_PAGES);
            --如果不是dsc环境，开启热页动态加载，关闭预读
            SP_SET_PARA_VALUE(2,'ENABLE_FREQROOTS',1);
            SP_SET_PARA_VALUE(2,'MULTI_PAGE_GET_NUM',1);
            SP_SET_PARA_VALUE(2,'PRELOAD_SCAN_NUM',0);
            SP_SET_PARA_VALUE(2,'PRELOAD_EXTENT_NUM',0);
        END IF;
        --修改HASH相关参数
        SP_SET_PARA_VALUE(1,'HJ_BUF_GLOBAL_SIZE', HJ_BUF_GLOBAL_SIZE);
        SP_SET_PARA_VALUE(1,'HJ_BUF_SIZE', HJ_BUF_SIZE );
        SP_SET_PARA_VALUE(1,'HAGR_BUF_GLOBAL_SIZE',HAGR_BUF_GLOBAL_SIZE);
        SP_SET_PARA_VALUE(1,'HAGR_BUF_SIZE', HAGR_BUF_SIZE );
        --修改排序相关参数
        SP_SET_PARA_VALUE(2,'SORT_FLAG',SORT_FLAG);
        SP_SET_PARA_VALUE(2,'SORT_BLK_SIZE',SORT_BLK_SIZE);
        SP_SET_PARA_VALUE(2,'SORT_BUF_SIZE', SORT_BUF_SIZE);
        SP_SET_PARA_VALUE(2,'SORT_BUF_GLOBAL_SIZE', SORT_BUF_GLOBAL_SIZE);
        --修改其他内存参数
        SP_SET_PARA_VALUE(2,'RLOG_POOL_SIZE', RLOG_POOL_SIZE);
        SP_SET_PARA_VALUE(2,'CACHE_POOL_SIZE', CACHE_POOL_SIZE);
        SP_SET_PARA_VALUE(2,'DICT_BUF_SIZE', DICT_BUF_SIZE);
        SP_SET_PARA_VALUE(2,'VM_POOL_TARGET', 16384);
        SP_SET_PARA_VALUE(2,'SESS_POOL_TARGET', 16384);
        --修改实例相关参数
        SP_SET_PARA_VALUE(2,'USE_PLN_POOL', 1);
        SP_SET_PARA_VALUE(2,'ENABLE_MONITOR', 1);
        SP_SET_PARA_VALUE(2,'SVR_LOG', 0);
        SP_SET_PARA_VALUE(2,'TEMP_SIZE', 1024);
        SP_SET_PARA_VALUE(2,'TEMP_SPACE_LIMIT', 102400);
        SP_SET_PARA_VALUE(2,'MAX_SESSIONS', 1500);
        SP_SET_PARA_VALUE(2,'MAX_SESSION_STATEMENT', 20000);
        --性能要求高且大字段较少的业务场景建议设置为1，大字段多的场景设置为0
        if pk_cluster_mode = 1 then
            SP_SET_PARA_VALUE(2,'PK_WITH_CLUSTER',1);
        else
            SP_SET_PARA_VALUE(2,'PK_WITH_CLUSTER',0);
        end if;
        SP_SET_PARA_VALUE(2,'ENABLE_ENCRYPT',0);
        --修改优化器相关参数
        SP_SET_PARA_VALUE(2,'OLAP_FLAG',2);
        SP_SET_PARA_VALUE(2,'VIEW_PULLUP_FLAG',1);
        SP_SET_PARA_VALUE(2,'OPTIMIZER_MODE',1);
        SP_SET_PARA_VALUE(2,'ADAPTIVE_NPLN_FLAG',0);
        --开启并行PURGE
        SP_SET_PARA_VALUE(2,'PARALLEL_PURGE_FLAG',1);
        --开启手动并行
        SP_SET_PARA_VALUE(2,'PARALLEL_POLICY',2);
        --UNDO_RETENTION如果放大，可以适当调大UNDO_EXTENT_NUM。负载高的时候，减少文件系统的申请/释放操作。
        SP_SET_PARA_VALUE(2,'UNDO_EXTENT_NUM',16);
        --开启SQL 注入HINT功能
        SP_SET_PARA_VALUE(2,'ENABLE_INJECT_HINT',1);
        SP_SET_PARA_VALUE(2,'FAST_LOGIN',1);
        SP_SET_PARA_VALUE(2,'BTR_SPLIT_MODE',1);
        --关闭参数监控
        SP_SET_PARA_VALUE(2,'ENABLE_MONITOR_BP',0);
        --开启数据异步追踪
        SP_SET_PARA_VALUE(1,'SVR_LOG',1);
        --开启操作系统认证
        SP_SET_PARA_VALUE(2,'ENABLE_LOCAL_OSAUTH',1);
        IF EXISTS (SELECT 1 FROM v\$instance WHERE dsc_role <> 'NULL') THEN
            SP_SET_PARA_VALUE(2,'ENABLE_FREQROOTS',0);
            --2022年5月月度版以后版本放开数据页预加载参数，DSC环境下开启
            IF exists(select 1 
                   from v\$version 
                  where banner like '%-%' 
                    and REGEXP_SUBSTR(banner,'[^-]+',1,2)>='2022-05-25' 
                    and REGEXP_SUBSTR(banner,'[^-]+',1,3)>=161267) THEN
                SP_SET_PARA_VALUE(2,'MULTI_PAGE_GET_NUM',16);
                SP_SET_PARA_VALUE(2,'PRELOAD_SCAN_NUM',4);
                SP_SET_PARA_VALUE(2,'PRELOAD_EXTENT_NUM',5);
            ELSE
                SP_SET_PARA_VALUE(2,'MULTI_PAGE_GET_NUM',1);
                SP_SET_PARA_VALUE(2,'PRELOAD_SCAN_NUM',0);
                SP_SET_PARA_VALUE(2,'PRELOAD_EXTENT_NUM',0);
            END IF;
            SP_SET_PARA_VALUE(2,'DSC_N_POOLS',MEMORY_N_POOLS);
            IF EXISTS (SELECT * FROM V\$DM_INI WHERE PARA_NAME='DSC_GBS_REVOKE_OPT') THEN
                SP_SET_PARA_VALUE(2,'DSC_GBS_REVOKE_OPT',0);
            END IF;
            SP_SET_PARA_VALUE(2,'DSC_HALT_SYNC',0);
            SP_SET_PARA_VALUE(2,'DSC_N_CTLS',50000);
            SP_SET_PARA_VALUE(2,'DSC_ENABLE_MONITOR',0);
            SP_SET_PARA_VALUE(2,'TRX_DICT_LOCK_NUM',5);
            SP_SET_PARA_VALUE(2,'DIRECT_IO',1);
        END IF;
    ELSE
        --修改cpu相关参数
        PRINT 'SP_SET_PARA_VALUE(2,''WORKER_THREADS'','||v_cpus||');';
        PRINT 'SP_SET_PARA_VALUE(2,''TASK_THREADS'','||TASK_THREADS||');';
        PRINT 'SP_SET_PARA_VALUE(2,''IO_THR_GROUPS'','||IO_THR_GROUPS||');';
        --修改内存池相关参数
        PRINT 'SP_SET_PARA_VALUE(2,''MAX_OS_MEMORY'',       '||mem_per||');';
        PRINT 'SP_SET_PARA_VALUE(2,''MEMORY_POOL'',         '||MEMORY_POOL||');';
        PRINT 'SP_SET_PARA_VALUE(2,''MEMORY_N_POOLS'',      '||MEMORY_N_POOLS||');';
        PRINT 'SP_SET_PARA_VALUE(2,''MEMORY_TARGET'',       '||MEMORY_TARGET||');';
        --修改缓冲区相关参数
        PRINT 'SP_SET_PARA_VALUE(2,''BUFFER'',              '||BUFFER||');';
        --新版本已去掉MAX_BUFFER参数，如果存在就修改
        IF EXISTS (SELECT * FROM V\$DM_INI WHERE PARA_NAME='MAX_BUFFER') THEN
            PRINT 'SP_SET_PARA_VALUE(2,''MAX_BUFFER'',          '||MAX_BUFFER||');';
        END IF;
        PRINT 'SP_SET_PARA_VALUE(2,''BUFFER_POOLS'',        '||BUFFER_POOLS||');';
        PRINT 'SP_SET_PARA_VALUE(2,''RECYCLE'',        	   '||RECYCLE||');';
        PRINT 'SP_SET_PARA_VALUE(2,''RECYCLE_POOLS'',       '||RECYCLE_POOLS||');';
        --修改fast_pool相关参数，如果是dsc环境，适当放小，以免影响启动速度
        IF EXISTS (SELECT 1 FROM v\$instance WHERE dsc_role <> 'NULL') THEN
            PRINT 'SP_SET_PARA_VALUE(2,''FAST_POOL_PAGES'',  10000);';
            PRINT 'SP_SET_PARA_VALUE(2,''FAST_ROLL_PAGES'',   3000);';
        ELSE
            PRINT 'SP_SET_PARA_VALUE(2,''FAST_POOL_PAGES'',     '||FAST_POOL_PAGES||');';
            PRINT 'SP_SET_PARA_VALUE(2,''FAST_ROLL_PAGES'',     '||FAST_ROLL_PAGES||');';
            --如果不是dsc环境，开启热页动态加载，关闭预读
            PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_FREQROOTS'',1);';
            PRINT 'SP_SET_PARA_VALUE(2,''MULTI_PAGE_GET_NUM'',1);';
            PRINT 'SP_SET_PARA_VALUE(2,''PRELOAD_SCAN_NUM'',0);';
            PRINT 'SP_SET_PARA_VALUE(2,''PRELOAD_EXTENT_NUM'',0);';
        END IF;
        --修改内存检测参数为1 	
        PRINT 'SP_SET_PARA_VALUE(2,''MEMORY_MAGIC_CHECK'',       1);';
        --修改HASH相关参数
        PRINT 'SP_SET_PARA_VALUE(1,''HJ_BUF_GLOBAL_SIZE'',  '||HJ_BUF_GLOBAL_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(1,''HJ_BUF_SIZE'',        '||HJ_BUF_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(1,''HAGR_BUF_GLOBAL_SIZE'','||HAGR_BUF_GLOBAL_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(1,''HAGR_BUF_SIZE'',     '||HAGR_BUF_SIZE||');';
        --修改排序相关参数
        PRINT 'SP_SET_PARA_VALUE(2,''SORT_FLAG'','||SORT_FLAG||');';
        PRINT 'SP_SET_PARA_VALUE(2,''SORT_BLK_SIZE'','||SORT_BLK_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(2,''SORT_BUF_SIZE'',       '||SORT_BUF_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(2,''SORT_BUF_GLOBAL_SIZE'',       '||SORT_BUF_GLOBAL_SIZE||');';
        --修改其他内存参数
        PRINT 'SP_SET_PARA_VALUE(2,''RLOG_POOL_SIZE'',      '||RLOG_POOL_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(2,''CACHE_POOL_SIZE'',     '||CACHE_POOL_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(2,''DICT_BUF_SIZE'',       '||DICT_BUF_SIZE||');';
        PRINT 'SP_SET_PARA_VALUE(2,''VM_POOL_TARGET'',       16384);';
        PRINT 'SP_SET_PARA_VALUE(2,''SESS_POOL_TARGET'',       16384);';
        --修改实例相关参数
        PRINT 'SP_SET_PARA_VALUE(2,''USE_PLN_POOL'',        1);';
        PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_MONITOR'',      1);';
        PRINT 'SP_SET_PARA_VALUE(2,''SVR_LOG'',             0);';
        PRINT 'SP_SET_PARA_VALUE(2,''TEMP_SIZE'',           1024);';
        PRINT 'SP_SET_PARA_VALUE(2,''TEMP_SPACE_LIMIT'',    102400);';
        PRINT 'SP_SET_PARA_VALUE(2,''MAX_SESSIONS'',        1500);';
        PRINT 'SP_SET_PARA_VALUE(2,''MAX_SESSION_STATEMENT'', 20000);';
        --性能要求高且大字段较少的业务场景建议设置为1，大字段多的场景设置为0
        if pk_cluster_mode = 1 then
            PRINT 'SP_SET_PARA_VALUE(2,''PK_WITH_CLUSTER'',		1);';
        else
            PRINT 'SP_SET_PARA_VALUE(2,''PK_WITH_CLUSTER'',		0);';
        end if;
        PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_ENCRYPT'',0);';
        --修改优化器相关参数
        PRINT 'SP_SET_PARA_VALUE(2,''OLAP_FLAG'',2);';
        PRINT 'SP_SET_PARA_VALUE(2,''VIEW_PULLUP_FLAG'',1);';
        PRINT 'SP_SET_PARA_VALUE(2,''OPTIMIZER_MODE'',1);';
        PRINT 'SP_SET_PARA_VALUE(2,''ADAPTIVE_NPLN_FLAG'',0);';
        --开启并行PURGE
        PRINT 'SP_SET_PARA_VALUE(2,''PARALLEL_PURGE_FLAG'',1);';
        --开启手动并行
        PRINT 'SP_SET_PARA_VALUE(2,''PARALLEL_POLICY'',2);';
        --UNDO_RETENTION如果放大，可以适当调大UNDO_EXTENT_NUM。负载高的时候，减少文件系统的申请/释放操作。
        PRINT 'SP_SET_PARA_VALUE(2,''UNDO_EXTENT_NUM'',16);';
        --开启INJECT HINT功能
        PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_INJECT_HINT'',1);';
        PRINT 'SP_SET_PARA_VALUE(2,''BTR_SPLIT_MODE'',1);';
        PRINT 'SP_SET_PARA_VALUE(2,''FAST_LOGIN'',1);';
        --关闭参数监控
        PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_MONITOR_BP'',0);';
        --PRINT '开启数据异步追踪: ';
        PRINT 'SP_SET_PARA_VALUE(1,''SVR_LOG'',              1);';
        --PRINT '开启操作系统认证: ';
        PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_LOCAL_OSAUTH'',  1);';
        IF EXISTS (SELECT 1 FROM v\$instance WHERE dsc_role <> 'NULL') THEN
            PRINT 'SP_SET_PARA_VALUE(2,''ENABLE_FREQROOTS'',0);';
            --2022年5月月度版以后版本放开数据页预加载参数
            IF exists(select 1 
                   from v\$version 
                  where banner like '%-%' 
                    and REGEXP_SUBSTR(banner,'[^-]+',1,2)>='2022-05-25' 
                    and REGEXP_SUBSTR(banner,'[^-]+',1,3)>=161267) THEN
                PRINT 'SP_SET_PARA_VALUE(2,''MULTI_PAGE_GET_NUM'',16);';
                PRINT 'SP_SET_PARA_VALUE(2,''PRELOAD_SCAN_NUM'',4);';
                PRINT 'SP_SET_PARA_VALUE(2,''PRELOAD_EXTENT_NUM'',5);';
            ELSE
                PRINT 'SP_SET_PARA_VALUE(2,''MULTI_PAGE_GET_NUM'',1);';
                PRINT 'SP_SET_PARA_VALUE(2,''PRELOAD_SCAN_NUM'',0);';
                PRINT 'SP_SET_PARA_VALUE(2,''PRELOAD_EXTENT_NUM'',0);';
            END IF;
            PRINT 'SP_SET_PARA_VALUE(2,''DSC_N_POOLS'',' ||MEMORY_N_POOLS ||');';
            IF EXISTS (SELECT * FROM V\$DM_INI WHERE PARA_NAME='DSC_GBS_REVOKE_OPT') THEN
                PRINT 'SP_SET_PARA_VALUE(2,''DSC_GBS_REVOKE_OPT'',0);';
            END IF;
            PRINT 'SP_SET_PARA_VALUE(2,''DSC_HALT_SYNC'',0);';
            PRINT 'SP_SET_PARA_VALUE(2,''DSC_N_CTLS'',50000);';
            PRINT 'SP_SET_PARA_VALUE(2,''DSC_ENABLE_MONITOR'',0);';
            PRINT 'SP_SET_PARA_VALUE(2,''TRX_DICT_LOCK_NUM'',5);';
            PRINT 'SP_SET_PARA_VALUE(2,''DIRECT_IO'',1);';
        END IF;
    END IF;
    select MEMORY_TARGET+BUFFER+RECYCLE+HJ_BUF_GLOBAL_SIZE+HAGR_BUF_GLOBAL_SIZE+CACHE_POOL_SIZE +DICT_BUF_SIZE+SORT_BUF_GLOBAL_SIZE+RLOG_POOL_SIZE;
exception
when others then
    raise_application_error (-20001,substr( ' 执行失败, '||SQLCODE||' '||SQLERRM||' '||dbms_utility.format_error_backtrace , 1, 400));
    <<return_2000>> null;
end;
/
exit;
EOF
  chown dmdba.dinstall "${conf_script}"
  ls -lh "${conf_script}"

  # 创建数据库优化结果查询脚本
  echo
  color_printf blue "创建数据库优化结果查询脚本:"
  echo
  cat <<-EOF >"${query_script}"
	set echo off
set timing off
set lineshow off
set feedback off
set pagesize 1000
select PARA_NAME name,
       PARA_VALUE para_value,
       FILE_VALUE file_value
  from v\$DM_INI
 WHERE PARA_NAME IN ('WORKER_THREADS',
                     'TASK_THREADS',
                     'IO_THR_GROUPS',
                     'MAX_OS_MEMORY',
                     'MEMORY_POOL',
                     'MEMORY_N_POOLS',
                     'MEMORY_TARGET',
                     'BUFFER',
                     'MAX_BUFFER',
                     'BUFFER_POOLS',
                     'RECYCLE',
                     'RECYCLE_POOLS',
                     'FAST_POOL_PAGES',
                     'FAST_ROLL_PAGES',
                     'MEMORY_MAGIC_CHECK',
                     'HJ_BUF_GLOBAL_SIZE',
                     'HJ_BUF_SIZE',
                     'HAGR_BUF_GLOBAL_SIZE',
                     'HAGR_BUF_SIZE',
                     'SORT_FLAG',
                     'SORT_BLK_SIZE',
                     'SORT_BUF_SIZE',
                     'SORT_BUF_GLOBAL_SIZE',
                     'RLOG_POOL_SIZE',
                     'CACHE_POOL_SIZE',
                     'DICT_BUF_SIZE',
                     'VM_POOL_TARGET',
                     'SESS_POOL_TARGET',
                     'USE_PLN_POOL',
                     'ENABLE_MONITOR',
                     'SVR_LOG',
                     'TEMP_SIZE',
                     'TEMP_SPACE_LIMIT',
                     'MAX_SESSIONS',
                     'MAX_SESSION_STATEMENT',
                     'PK_WITH_CLUSTER',
                     'ENABLE_ENCRYPT',
                     'OLAP_FLAG',
                     'VIEW_PULLUP_FLAG',
                     'OPTIMIZER_MODE',
                     'ADAPTIVE_NPLN_FLAG',
                     'PARALLEL_PURGE_FLAG',
                     'PARALLEL_POLICY',
                     'UNDO_EXTENT_NUM',
                     'ENABLE_INJECT_HINT',
                     'BTR_SPLIT_MODE',
                     'FAST_LOGIN',
                     'ENABLE_MONITOR_BP',
                     'MULTI_PAGE_GET_NUM',
                     'PRELOAD_SCAN_NUM',
                     'PRELOAD_EXTENT_NUM',
                     'DSC_N_POOLS',
                     'DSC_GBS_REVOKE_OPT',
                     'DSC_HALT_SYNC',
                     'DSC_N_CTLS',
                     'DSC_ENABLE_MONITOR',
                     'TRX_DICT_LOCK_NUM',
                     'ENABLE_FREQROOTS',
                     'DIRECT_IO')
union all
select 'MAL_CHECK_INTERVAL' ,
       'placeholder',
       CASE WHEN COUNT(MAL_CHECK_INTERVAL)=0 THEN 'NULL' ELSE (SELECT MAL_CHECK_INTERVAL FROM V\$MAL_SYS LIMIT 1) END
  FROM V\$MAL_SYS
union all
select 'MAL_CONN_FAIL_INTERVAL' ,
       'placeholder',
       CASE WHEN COUNT(MAL_CONN_FAIL_INTERVAL)=0 THEN 'NULL' ELSE (SELECT MAL_CONN_FAIL_INTERVAL FROM V\$MAL_SYS LIMIT 1) END
  from V\$MAL_SYS
union all
select 'MAL_BUF_SIZE' ,
       'placeholder',
       CASE WHEN COUNT(MAL_BUF_SIZE)=0 THEN 'NULL' ELSE (SELECT MAL_BUF_SIZE FROM V\$MAL_SYS LIMIT 1) END
  from V\$MAL_SYS
union all
select 'MAL_SYS_BUF_SIZE' ,
       'placeholder',
       CASE WHEN COUNT(MAL_SYS_BUF_SIZE)=0 THEN 'NULL' ELSE (SELECT MAL_SYS_BUF_SIZE FROM V\$MAL_SYS LIMIT 1) END
  from V\$MAL_SYS
union all
select 'MAL_COMPRESS_LEVEL' ,
       'placeholder',
       CASE WHEN COUNT(MAL_COMPRESS_LEVEL)=0 THEN 'NULL' ELSE (SELECT MAL_COMPRESS_LEVEL FROM V\$MAL_SYS LIMIT 1) END
  from V\$MAL_SYS
union all
select 'MAL_TEMP_PATH' ,
       'placeholder',
       CASE WHEN COUNT(MAL_TEMP_PATH)=0 THEN 'NULL' ELSE (SELECT MAL_TEMP_PATH||' ' FROM V\$MAL_SYS LIMIT 1) END
  from V\$MAL_SYS
union all
select 'MAL_VPOOL_SIZE' ,
       'placeholder',
       CASE WHEN COUNT(MAL_VPOOL_SIZE)=0 THEN 'NULL' ELSE (SELECT MAL_VPOOL_SIZE FROM V\$MAL_SYS LIMIT 1) END
  from V\$MAL_SYS
union all
  select 'MAL_INST_NAME' ,
         'placeholder',
         listagg(to_char(MAL_INST_NAME ),',') within GROUP (order by MAL_INST_NAME)
    from V\$dm_MAL_ini
union all
  select 'MAL_HOST' ,
         'placeholder',
         listagg(to_char(MAL_HOST ),',') within GROUP (order by MAL_HOST)
    from V\$dm_MAL_ini
union all
  select 'MAL_PORT' ,
         'placeholder',
         listagg(to_char(MAL_PORT ),',') within GROUP (order by MAL_PORT)
    from V\$dm_MAL_ini
union all
  select 'MAL_INST_HOST' ,
         'placeholder',
         listagg(to_char(MAL_INST_HOST ),',') within GROUP (order by MAL_INST_HOST)
    from V\$dm_MAL_ini
union all
  select 'MAL_INST_PORT' ,
         'placeholder',
         listagg(to_char(MAL_INST_PORT ),',') within GROUP (order by MAL_INST_PORT)
    from V\$dm_MAL_ini
union all
  select 'MAL_DW_PORT' ,
         'placeholder',
         listagg(to_char(MAL_DW_PORT ),',') within GROUP (order by MAL_DW_PORT)
    from V\$dm_MAL_ini
UNION ALL
select 'ARCH_DEST',
       'placeholder',
       CASE WHEN COUNT(ARCH_DEST)=0 THEN 'NULL' ELSE (SELECT ARCH_DEST FROM V\$dm_arch_ini LIMIT 1) END
  from V\$dm_arch_ini;
exit;
EOF
  chown dmdba.dinstall "${query_script}"
  ls -lh "${query_script}"
}

function optimize_dmdb() {
  #脚本路径
  SCRIPTS_DIR="/home/dmdba/scripts"
  #执行脚本的函数
  execute_script() {
    local script_name="$1"
    local script_path="${SCRIPTS_DIR}/${script_name}"
    su - dmdba -c "disql -S SYSDBA/SYSDBA:$port_num '\`$script_path'"
  }

  if [[ $dm_install_mode = "single" ]]; then
    log_print "配置数据库归档" ""
    execute_script "conf_arch.sql"
  fi

  backup_if_enabled() {
    local ip=${1}
    if [[ $enable_arch = "Y" ]]; then
      log_print "在 ${ip} 创建备份" ""
      if [[ $backup_mode = "1" ]]; then
        execute_script "conf_fullbackup.sql"
      elif [[ $backup_mode = "2" ]]; then
        execute_script "conf_incrbackup.sql"
      fi
    fi
  }

  if [[ $dm_install_mode = "dsc" ]]; then
    if [[ $enable_arch = "Y" ]]; then
      lastIndex=$((${#wc_pubip_arr[@]} - 1))
      backup_if_enabled "${wc_pubip_arr[$lastIndex]}"
    fi
  elif [[ $dm_install_mode = "datawatch" ]]; then
    if [[ $node_num = 1 && $enable_arch = "Y" ]]; then
      backup_if_enabled "${wc_pubip_arr[0]}"
    fi
  else
    backup_if_enabled "127.0.0.1"
  fi

  log_print "优化数据库基础参数" ""
  execute_script "conf_para.sql"

  echo
  color_printf blue "重启数据库，优化参数生效"
  echo
  if [[ $dm_install_mode = "dsc" ]]; then
    color_printf red "关闭数据库"
    echo
    su - dmdba -c "$env_dm_home/bin/dmcssm $data_dir/$dsc_db_name/dmcssm.ini" <<-DMCSSM
ep stop GRP_$dsc_db_name
exit
DMCSSM
    while true; do
      sleep 5s
      PID=$(pgrep -f "$data_dir"/"$dsc_db_name"/"$dsc_ep_name"0_config/dm.ini)
      [ ! "$PID" ] && break
    done
    color_printf red "启动数据库"
    echo
    su - dmdba -c "$env_dm_home/bin/dmcssm $data_dir/$dsc_db_name/dmcssm.ini" <<-DMCSSM
ep startup GRP_$dsc_db_name
exit
DMCSSM
    config_file="$data_dir/$dsc_db_name/$dsc_ep_name"0_config/dm.ini
    timeout=120 # 等待2分钟
    start_time=$(date +%s)
    while true; do
      sleep 5s
      elapsed_time=$(($(date +%s) - start_time))
      if [ $elapsed_time -ge $timeout ]; then
        echo "超时：无法建立数据库连接"
        exit 1
      fi
      PID=$(pgrep -f "$config_file")
      if [ -n "$PID" ]; then
        connResult=$(su - dmdba -c "disql -S -L SYSDBA/SYSDBA:$port_num -c \"set heading off\" -e 'select 1'" | tail -1)
        if [ "$connResult" = 1 ]; then
          break
        fi
      fi
    done
  else
    su - dmdba -c "$env_dm_home/bin/DmService$db_name restart"
  fi

  log_print "创建DMDBA用户，密码：$sysdba_pwd" ""
  execute_script "ct_dbuser.sql"

  if [[ $dm_install_mode = "single" || $dm_install_mode = "dsc" ]]; then
    log_print "查询数据库优化结果：" ""
    su - dmdba -c "disql -S SYSDBA/'\"$sysdba_pwd\"':$port_num '\`/home/dmdba/scripts/query_dm.sql'"
  fi
}
#==============================================================#
#                    数据守护和DSC其他节点执行脚本                      #
#==============================================================#
function other_node_shell() {
  local node_script="$software_dir/dwnode.sh"

  for ((i = 1; i < ${#wc_pubip_arr[@]}; i++)); do
    log_print "配置节点: ${wc_pubip_arr[$i]}" ""
    color_printf blue "节点 ${wc_pubip_arr[$i]} 开始配置:"
    ssh -t "${wc_pubip_arr[$i]}" "cd $software_dir && sh $node_script"
    echo
    color_printf blue "节点 ${wc_pubip_arr[$i]} 配置完成."
  done

  if [[ $dw_monitor_ip ]]; then
    local monitor_script="$software_dir/dwmonitor_node.sh"
    log_print "配置主机: $dw_monitor_ip" ""
    color_printf blue "开始配置监视器:"
    ssh -t "$dw_monitor_ip" "cd $software_dir && sh $monitor_script"
    color_printf blue "监视器完成配置."
  fi
}

#==============================================================#
#                      数据守护主库脱机备份分发                    #
#==============================================================#
function dw_pri_backup() {
  log_print "数据守护主库脱机备份" ""
  color_printf blue "检查数据库进程是否存在："
  echo
  if pgrep -f "$data_dir/$db_name/dm.ini" >/dev/null; then
    color_printf yellow "数据库进程存在，尝试关闭主数据库："
    echo
    su - dmdba -c "$env_dm_home/bin/DmService$db_name stop"
    echo
    # 检查数据库是否已经停止
    while pgrep -f "$data_dir/$db_name/dm.ini" >/dev/null; do
      sleep 1
    done
  else
    color_printf green "数据库进程不存在，无需关闭"
    echo
  fi
  color_printf blue "主库进行脱机 dmrman 备份："
  echo
  su - dmdba -c "$env_dm_home/bin/dmrman CTLSTMT=\"BACKUP DATABASE '$data_dir/$db_name/dm.ini' FULL TO BACKUP_FILE1 BACKUPSET '$backup_dir/BACKUP_FILE_01'\""
  echo
  color_printf blue "主库查看脱机备份文件："
  echo
  ls -la "$backup_dir"/BACKUP_FILE_01
}

#==============================================================#
#                      数据守护备机脱机恢复                       #
#==============================================================#
function dw_std_restore() {
  log_print "数据守护备库脱机恢复" ""
  color_printf blue "检查数据库进程是否存在："
  echo
  if pgrep -f "$data_dir/$db_name/dm.ini" >/dev/null; then
    color_printf yellow "数据库进程存在，尝试关闭主数据库："
    echo
    su - dmdba -c "$env_dm_home/bin/DmService$db_name stop"
    echo
    # 检查数据库是否已经停止
    while pgrep -f "$data_dir/$db_name/dm.ini" >/dev/null; do
      sleep 1
    done
  else
    color_printf green "数据库进程不存在，无需关闭"
    echo
  fi
  color_printf blue "拷贝主库的备份文件并授权："
  echo
  ## 获取节点一的备份文件
  scp -o StrictHostKeyChecking=no -r "${wc_pubip_arr[0]}":"$backup_dir"/BACKUP_FILE_01 "$backup_dir"
  chown -R dmdba:dinstall "$backup_dir"
  echo
  color_printf blue "备库执行脱机数据库 dmrman 还原："
  echo
  su - dmdba -c "$env_dm_home/bin/dmrman CTLSTMT=\"RESTORE DATABASE '$data_dir/$db_name/dm.ini' FROM BACKUPSET '$backup_dir/BACKUP_FILE_01'\""
  echo
  color_printf blue "备库执行 dmrman 数据库更新："
  echo
  su - dmdba -c "$env_dm_home/bin/dmrman CTLSTMT=\"RECOVER DATABASE '$data_dir/$db_name/dm.ini' UPDATE DB_MAGIC\""
}

#==============================================================#
#                      数据守护主备配置参数文件                    #
#==============================================================#
function dw_conf_para() {
  log_print "数据守护主备配置参数文件" ""
  ## 监视器只需要配置 dmmonitor.ini 参数即可
  if [[ $node_num = -1 ]]; then
    ## 备库配置监视器 dmmonitor.ini 参数
    color_printf blue "监视器主机配置 dmmonitor.ini 参数："
    echo
    ## 写入头内容
    su - dmdba <<-DMMON
cat<<-EOF>$env_dm_home/dmmonitor_$dw_group_name.ini
MON_DW_CONFIRM = 1
MON_LOG_PATH = $env_dm_home/log
MON_LOG_INTERVAL = 60
MON_LOG_FILE_SIZE = 32
MON_LOG_SPACE_LIMIT = 2048

[$dw_group_name]
MON_INST_OGUID = $oguid
EOF
DMMON
    ## 数组循环输出
    for ((i = 0; i < ${#wc_pubip_arr[@]}; i++)); do
      su - dmdba <<-DMMON
cat<<-EOF>>$env_dm_home/dmmonitor_$dw_group_name.ini
MON_DW_IP = ${wc_malip_arr[$i]}:$mal_dw_port_num
EOF
DMMON
    done

    cat "$env_dm_home"/dmmonitor_"$dw_group_name".ini

    ## 注册 dmmonitor 服务
    echo
    color_printf blue "监视主机注册监视器服务："
    echo
    if [[ $os_version != "6" ]]; then
      if [[ -e /usr/lib/systemd/system/DmMonitorService"${dw_group_name}".service ]]; then
        rm -f /usr/lib/systemd/system/DmMonitorService"${dw_group_name}".service
      fi
    fi
    "$env_dm_home"/script/root/dm_service_installer.sh -t dmmonitor -monitor_ini "$env_dm_home"/dmmonitor_"$dw_group_name".ini -p "$dw_group_name"
    if [[ $os_version = "6" ]]; then
      ln -sf /etc/rc.d/init.d/DmMonitorService"$dw_group_name" "$env_dm_home"/bin/DmMonitorService"$dw_group_name"
      chmod 755 "$env_dm_home"/bin/DmMonitorService"$dw_group_name"
    fi

    ## 启动监视进程
    echo
    color_printf blue "监视主机启动监视进程："
    echo
    su - dmdba -c "$env_dm_home/bin/DmMonitorService$dw_group_name start"
  else
    ## 主备库配置 dm.ini 参数
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库配置 dm.ini 参数："
    else
      color_printf blue "备库配置 dm.ini 参数："
    fi
    echo
    su - dmdba <<-DM
  sed -i 's/ALTER_MODE_STATUS               = 0/ALTER_MODE_STATUS               = 1/g' $data_dir/$db_name/dm.ini
  sed -i 's/ENABLE_OFFLINE_TS               = 1/ENABLE_OFFLINE_TS               = 2/g' $data_dir/$db_name/dm.ini
  sed -i 's/MAL_INI                         = 0/MAL_INI                         = 1/g' $data_dir/$db_name/dm.ini
  sed -i 's/ARCH_INI                        = 0/ARCH_INI                        = 1/g' $data_dir/$db_name/dm.ini
DM

    grep <"$data_dir"/"$db_name"/dm.ini "ALTER_MODE_STATUS" &&
      grep <"$data_dir"/"$db_name"/dm.ini "ENABLE_OFFLINE_TS" &&
      grep <"$data_dir"/"$db_name"/dm.ini "MAL_INI" &&
      grep <"$data_dir"/"$db_name"/dm.ini "ARCH_INI"

    ## 主备库配置 dmmal.ini 参数
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库配置 dmmal.ini 参数："
    else
      color_printf blue "备库配置 dmmal.ini 参数："
    fi
    echo
    ## 写入头内容
    su - dmdba <<-DMMAL
  cat <<-EOF >$data_dir/$db_name/dmmal.ini
MAL_CHECK_INTERVAL = 15
MAL_CONN_FAIL_INTERVAL = 15
EOF
DMMAL
    ## 数组循环输出
    for ((i = 0; i < ${#wc_pubip_arr[@]}; i++)); do
      su - dmdba <<-DMMAL
  cat <<-EOF >>$data_dir/$db_name/dmmal.ini

[MAL_INST$((i + 1))]
MAL_INST_NAME = $dw_instance_name$((i + 1))
MAL_HOST = ${wc_malip_arr[$i]}
MAL_PORT = $mal_port_num
MAL_INST_HOST = ${wc_pubip_arr[i]}
MAL_INST_PORT = $port_num
MAL_DW_PORT = $mal_dw_port_num
MAL_INST_DW_PORT = $mal_inst_dw_port_num
EOF
DMMAL
    done

    cat "$data_dir"/"$db_name"/dmmal.ini

    ## 主备库配置 dmarch.ini 参数
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库配置 dmarch.ini 参数："
    else
      color_printf blue "备库配置 dmarch.ini 参数："
    fi
    echo
    ## 写入本地归档内容
    if [[ $dw_monitor_ip ]]; then
      su - dmdba <<-DMARCH
cat<<-EOF>$data_dir/$db_name/dmarch.ini
ARCH_WAIT_APPLY = 1
EOF
DMARCH
    else
      su - dmdba <<-DMARCH
cat<<-EOF>$data_dir/$db_name/dmarch.ini
ARCH_WAIT_APPLY = 0
EOF
DMARCH
    fi
    su - dmdba <<-DMARCH
cat<<-EOF>>$data_dir/$db_name/dmarch.ini

[ARCHIVE_LOCAL1]
        ARCH_TYPE            = LOCAL
        ARCH_DEST            = $arch_dir/$db_name
        ARCH_FILE_SIZE       = $log_size
        ARCH_SPACE_LIMIT     = $space_limit
EOF
DMARCH
    ## 数组循环输出
    a=0
    for ((i = 1; i <= ${#wc_pubip_arr[@]}; i++)); do
      if [[ $i != "$node_num" ]]; then
        a=$((a + 1))
        su - dmdba <<-DMARCH
cat<<-EOF>>$data_dir/$db_name/dmarch.ini

[ARCHIVE_REALTIME$a]
ARCH_TYPE = REALTIME
ARCH_DEST = $dw_instance_name$i
EOF
DMARCH
      fi
    done

    cat "$data_dir"/"$db_name"/dmarch.ini

    ## 主备库配置 dmwatcher.ini 参数
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库配置 dmwatcher.ini 参数："
    else
      color_printf blue "备库配置 dmwatcher.ini 参数："
    fi
    echo
    su - dmdba <<-DMWATCH
cat<<-EOF>$data_dir/$db_name/dmwatcher.ini
[$dw_group_name]
DW_TYPE = GLOBAL
DW_MODE = $dw_mode
DW_ERROR_TIME = 10
INST_ERROR_TIME = 10
INST_OGUID = $oguid
INST_INI = $data_dir/$db_name/dm.ini
INST_AUTO_RESTART = 1
INST_STARTUP_CMD = $env_dm_home/bin/DmService$db_name start
EOF
DMWATCH

    cat "$data_dir"/"$db_name"/dmwatcher.ini

    ## 主备库配置监视器 dmmonitor.ini 参数
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库配置 dmmonitor.ini 参数："
    else
      color_printf blue "备库配置 dmmonitor.ini 参数："
    fi
    echo
    ## 写入头内容
    su - dmdba <<-DMMON
cat<<-EOF>$data_dir/$db_name/dmmonitor_$dw_group_name.ini
MON_DW_CONFIRM = 0
MON_LOG_PATH = $env_dm_home/log
MON_LOG_INTERVAL = 60
MON_LOG_FILE_SIZE = 32
MON_LOG_SPACE_LIMIT = 2048

[$dw_group_name]
MON_INST_OGUID = $oguid
EOF
DMMON
    ## 数组循环输出
    for ((i = 0; i < ${#wc_pubip_arr[@]}; i++)); do
      su - dmdba <<-DMMON
cat<<-EOF>>$data_dir/$db_name/dmmonitor_$dw_group_name.ini
MON_DW_IP = ${wc_malip_arr[$i]}:$mal_dw_port_num
EOF
DMMON
    done

    cat "$data_dir"/"$db_name"/dmmonitor_"$dw_group_name".ini

    ## 注册 dmwatcher 服务
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库注册 dmwatcher 服务："
    else
      color_printf blue "备库注册 dmwatcher 服务："
    fi
    echo
    if [[ $os_version != "6" ]]; then
      if [[ -e /usr/lib/systemd/system/DmWatcherService"${dw_group_name}".service ]]; then
        rm -f /usr/lib/systemd/system/DmWatcherService"${dw_group_name}".service
      fi
    fi
    "$env_dm_home"/script/root/dm_service_installer.sh -t dmwatcher -watcher_ini "$data_dir"/"$db_name"/dmwatcher.ini -p "$dw_group_name"
    if [[ $os_version = "6" ]]; then
      ln -sf /etc/rc.d/init.d/DmWatcherService"$dw_group_name" "$env_dm_home"/bin/DmWatcherService"$dw_group_name"
      chmod 755 "$env_dm_home"/bin/DmWatcherService"$dw_group_name"
    fi
    ## 主备库启动到 mount 状态
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库启动到 mount 状态："
    else
      color_printf blue "备库启动到 mount 状态："
    fi
    echo
    su - dmdba -c "$env_dm_home/bin/DmService$db_name start mount"

    ## 修改主备库模式
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库修改模式："
    else
      color_printf blue "备库修改模式："
    fi
    echo
    if [[ $node_num = 1 ]]; then
      cat <<-EOF >/home/dmdba/scripts/dw_conf_pri.sql
set echo off
set timing off
set feedback off
set lineshow off
sp_set_oguid($oguid);
alter database primary;
SP_SET_PARA_VALUE(1, 'ALTER_MODE_STATUS', 0);
exit;
EOF
      su - dmdba -c "disql -S SYSDBA/'\"$sysdba_pwd\"':$port_num '\`/home/dmdba/scripts/dw_conf_pri.sql'"
    else
      cat <<-EOF >/home/dmdba/scripts/dw_conf_std.sql
set echo off
set timing off
set feedback off
set lineshow off
sp_set_oguid($oguid);
alter database standby;
SP_SET_PARA_VALUE(1, 'ALTER_MODE_STATUS', 0);
exit;
EOF
      su - dmdba -c "disql -S SYSDBA/'\"$sysdba_pwd\"':$port_num '\`/home/dmdba/scripts/dw_conf_std.sql'"
    fi

    ## 启动守护进程
    echo
    if [[ $node_num = 1 ]]; then
      color_printf blue "主库启动守护进程："
    else
      color_printf blue "备库启动守护进程："
    fi
    echo
    su - dmdba -c "$env_dm_home/bin/DmWatcherService$dw_group_name start"
    echo

    if [[ $node_num = 1 ]]; then
      log_print "查询数据库优化结果：" ""
      su - dmdba -c "disql -S SYSDBA/'\"$sysdba_pwd\"':$port_num '\`/home/dmdba/scripts/query_dm.sql'"
    fi
  fi
}
#==============================================================#
#                          DSC部署                             #
#==============================================================#
## 创作不易，且行且珍惜，2022年进步劳动成果之一
#==============================================================#
#                         定义全局常量                         #
#==============================================================#
UDEV_RULES_PATH="/etc/udev/rules.d"            # udev规则文件路径
ASM_DEVICE_PATH="/dev/asmdisk"                 # ASM设备组路径
DM_PERMISSIONS_RULES="12-dm-permissions.rules" # udev权限规则文件名
DM_ASMDEVICES_RULES="99-dm-asmdevices.rules"   # udev ASM设备规则文件名
MULTIPATH_CONF_PATH="/etc/multipath.conf"      # multipath配置文件路径
#==============================================================#
#                         Get UUID                             #
#==============================================================#
function get_uuid() {
  local uuid
  case $os_version in
  6)
    uuid=$(/sbin/scsi_id -g -u "$1")
    ;;
  *)
    if [[ $1 =~ [0-9] ]]; then
      uuid=$(udevadm info --query=all --name="$1" | awk -F'=' '/ID_PART_ENTRY_UUID/ {print $2}')
    else
      uuid=$(/usr/lib/udev/scsi_id -g -u "$1")
    fi
    ;;
  esac

  printf '%s' "$uuid"
}

function get_dm_uuid() {
  udevadm info --query=all --name=/dev/mapper/"$1" | awk -F'=' '/DM_UUID/ {print $2}'
}

#==============================================================#
#                       检查 磁盘是否具有唯一uuid                #
#==============================================================#
function check_disk() {
  local declar_opt1="$1" # 第一个参数，指定要检查的磁盘选项
  local declar_opt2="$2" # 第二个参数，指定要检查的磁盘选项
  local disk_list_tmp=() # 用于存储待检查的磁盘列表

  if [ -z "$fil_disk" ]; then
    echo
    echo "校验 $3 磁盘，请等待！！！"
  fi

  # 处理磁盘列表参数
  if [[ $3 =~ "," ]]; then
    IFS=',' read -ra disk_list <<<"$3" # 将以逗号分割的字符串转换为数组
    for disk in "${disk_list[@]}"; do  # 循环遍历磁盘列表
      disk_list_arr+=("$disk")         # 将不是选项参数的磁盘加入待检查列表
    done
  else
    for disk in "${@:3}"; do   # 从第三个参数开始循环遍历磁盘列表
      disk_list_arr+=("$disk") # 将不是选项参数的磁盘加入待检查列表
    done
  fi
  # 遍历待检查的磁盘列表
  for disk in "${disk_list_arr[@]}"; do
    local uuid=""
    if [[ $disk =~ "dev" ]]; then
      uuid=$(get_uuid "$disk") # 获取磁盘的UUID
    else
      uuid=$(get_dm_uuid "$disk") # 获取设备映射的UUID
    fi
    if [[ $uuid = "" ]]; then
      echo
      color_printf red "${RED}请检查参数 [$declar_opt1, $declar_opt2] 的值 $disk 是否正确！${NC}" # 如果UUID为空，打印错误信息并退出函数
      echo
      exit 99
    fi
    # 该脚本检查磁盘是否已经在磁盘列表中，如果找到则退出脚本
    if [ -z "$fil_disk" ]; then
      local found=false
      for tmp_disk in "${disk_list_tmp[@]}"; do # 检查是否有重复的磁盘
        if [[ $(get_uuid "$disk") = $(get_uuid "$tmp_disk") ]]; then
          found=true
          break
        fi
      done
      if $found; then
        echo
        color_printf red "${RED}在参数 [$declar_opt1, $declar_opt2] 中检测到重复的磁盘：$disk${NC}" # 如果有重复的磁盘，打印错误信息并退出函数
        echo
        exit 99
      fi
    fi
    disk_list_tmp+=("$disk") # 将磁盘加入临时磁盘列表
  done

  #清空磁盘
  if [[ $format_disk = "Y" ]]; then # 是否清空磁盘，如果值是Y，就清空磁盘
    for disk in "${disk_list_tmp[@]}"; do
      while true; do
        sleep 5s
        dd if=/dev/zero of="$disk" bs=100M count=10 >/dev/null 2>&1 # 使用dd命令写入0以清空磁盘
        if [ $? -eq 0 ]; then break; fi                             # 如果命令执行成功，跳出循环
      done
    done
  fi

  if [[ "$fil_disk" != "" ]]; then
    local disk_storage_arr=()
    for disk in "${disk_list_tmp[@]}"; do
      # 获取磁盘的存储信息，并将信息加入数组
      uuid=$(get_uuid "$disk")
      if [[ ! " ${disk_storage_arr[*]} " =~ $uuid ]]; then # check if UUID already exists in array
        disk_storage_arr+=("$uuid $disk $(disk_storage "$disk")")
      fi
    done
    IFS=$'\n' sorted_disk_storage_arr=($(sort -k3n -k2 <<<"${disk_storage_arr[*]}")) # 对存储信息按照大小排序
    printf "${GREEN}%-40s %-20s %s${NC}\n" "UUID" "磁盘" "存储大小"                        # 打印表头
    for disk_storage in "${sorted_disk_storage_arr[@]}"; do
      local uuid
      uuid=$(echo "$disk_storage" | awk '{print $1}')
      local disk
      disk=$(echo "$disk_storage" | awk '{print $2}')
      local storage
      storage=$(echo "$disk_storage" | awk '{print $3}')
      printf "%-40s %-20s %s\n" "$uuid" "$disk" "$storage"
    done
  fi
}
#==============================================================#
#配置ASM磁盘
#==============================================================#
function dsc_conf_asmdisk() {

  # 根据变量 multipath 的值设置规则文件名
  rules_file=""
  if [[ $multipath = "Y" ]]; then
    rules_file="$UDEV_RULES_PATH/$DM_PERMISSIONS_RULES"
  else
    rules_file="$UDEV_RULES_PATH/$DM_ASMDEVICES_RULES"
  fi

  # 定义参数变量
  local device_name="$1"
  local symlink_name="$2"

  # get_uuid_or_dm_uuid
  get_uuid_or_dm_uuid() {
    local device_path=$1 # 设备路径
    local uuid
    if [[ $multipath == "Y" ]]; then # 如果启用了多路径，并且设备路径包含dev字符串
      if [[ $device_path =~ "/dev" ]]; then
        uuid="mpath-"$(get_uuid "$device_path") # 获取设备的普通UUID
      else
        uuid=$(get_dm_uuid "$device_path") # 获取设备的多路径UUID
      fi
    else
      uuid=$(get_uuid "$device_path") # 获取设备的普通UUID
    fi
    echo "$uuid"
  }

  # 定义内嵌函数
  # 创建 multipath 规则
  create_multipath_rule() {
    local uuid
    uuid=$(get_uuid "$1")
    cat <<EOF >>$MULTIPATH_CONF_PATH
  multipath {
    wwid   "$uuid"
    alias  $2
  }
EOF
  }

  # 创建符号链接规则
  create_symlink_rule() {
    echo "KERNEL==\"dm-*\", ENV{DM_UUID}==\"$(get_uuid_or_dm_uuid "$device_name")\", SYMLINK+=\"asmdisk/$symlink_name\", OWNER=\"dmdba\", GROUP=\"dinstall\", MODE=\"0660\"" >>$rules_file
  }

  # 创建 SCSI ID 规则
  create_scsi_id_rule() {
    echo "KERNEL==\"sd*\", BUS==\"scsi\", PROGRAM==\"/sbin/scsi_id --whitelisted --replace-whitespace --device=/dev/\$name\", RESULT==\"$(get_uuid_or_dm_uuid "$device_name")\", SYMLINK+=\"asmdisk/$symlink_name\", OWNER=\"dmdba\", GROUP=\"dinstall\", MODE=\"0660\"" >>$rules_file
  }

  # 添加设备符号链接
  creeate_device_symlink() {
    echo "KERNEL==\"sd*\", ENV{ID_PART_ENTRY_UUID}==\"$(get_uuid_or_dm_uuid "$device_name")\", SYMLINK+=\"asmdisk/$symlink_name\", OWNER=\"dmdba\", GROUP=\"dinstall\", MODE=\"0660\"" >>$rules_file
  }

  # 创建块设备规则
  create_block_rule() {
    echo "KERNEL==\"sd*\", SUBSYSTEM==\"block\", PROGRAM==\"/usr/lib/udev/scsi_id --whitelisted --replace-whitespace --device=/dev/\$name\", RESULT==\"$(get_uuid_or_dm_uuid "$device_name")\", SYMLINK+=\"asmdisk/$symlink_name\", OWNER=\"dmdba\", GROUP=\"dinstall\", MODE=\"0660\"" >>$rules_file
  }

  # 创建udev规则文件
  if [[ $multipath = "Y" ]]; then
    if [[ $1 =~ "dev" ]]; then
      # 调用内嵌函数创建 multipath 规则和符号链接规则
      create_multipath_rule "$device_name" "$symlink_name"
    fi
    # 检查是否存在属于 dmdba 或 dinstall 的现有设备
    if find /dev/ -maxdepth 1 \( -user "dmdba" -or -group "dinstall" \) >/dev/null 2>&1; then
      create_symlink_rule
      return
      # 检查 /dev/asmdisk 是否存在以 "dm" 开头的文件
    elif [[ ! -d $ASM_DEVICE_PATH || $(find $ASM_DEVICE_PATH -mindepth 1 -name 'dm*' | wc -l) -eq 0 ]]; then
      create_symlink_rule
      return
    fi
  else
    # 根据操作系统版本和可用信息创建 udev 规则
    if [[ $os_version = "6" ]]; then
      create_scsi_id_rule
    else
      if [[ $device_name =~ [0-9] ]]; then
        creeate_device_symlink
      else
        create_block_rule
      fi
    fi
  fi
}
#==============================================================#
## 配置udev生效
#==============================================================#
function dsc_conf_udev() {
  if [[ $multipath = "Y" && ${disk_arr[0]} =~ "dev" ]]; then
    log_print "查看multipath聚合磁盘" "" # 打印查看multipath聚合磁盘的信息
    multipath -F
    multipath -r >/dev/null 2>&1
    multipath -ll
  fi
  # 启动udev
  while true; do
    if [[ $os_version = "6" ]]; then
      echo
      start_udev # 在CentOS 6上启动udev服务
    else
      /sbin/udevadm control --reload-rules                 # 重新加载udev规则
      /sbin/udevadm trigger --type=devices --action=change # 触发设备变化事件
    fi
    sleep 5s
    if [[ $(find $ASM_DEVICE_PATH -mindepth 1 -name 'dm*' | wc -l) -gt 0 ]]; then
      log_print "查看udev磁盘" "" # 打印查看udev磁盘的信息
      ls -lh $ASM_DEVICE_PATH
      break
    fi
  done
}
#==============================================================#
### 创建ASM磁盘数组
#==============================================================#
function dsc_conf_disk_arr() {

  if [[ $node_num -gt 1 ]]; then
    log_print "拷贝UDEV规则文件，并启动UDEV" ""
  else
    log_print "创建UDEV规则文件，并启动UDEV" ""
  fi

  DEFAULT_MULTIPATH_CONF=$(
    cat <<'EOF'
defaults {
  polling_interval        30
  failback                immediate
  no_path_retry           5
  rr_min_io               100
  path_checker            tur
  user_friendly_names     yes
}
EOF
  )

  # 根据变量 multipath 的值设置规则文件名
  # 规则文件路径
  rules_file=""
  if [[ $multipath = "Y" ]]; then
    rules_file="$UDEV_RULES_PATH/$DM_PERMISSIONS_RULES"
  else
    rules_file="$UDEV_RULES_PATH/$DM_ASMDEVICES_RULES"
  fi
  # 如果规则文件存在
  if [[ -f "$rules_file" ]]; then
    # 生成备份文件名
    backup_file="$UDEV_RULES_PATH/rules_file.$(date -Iseconds)"
    # 备份规则文件
    if cp -r "$rules_file" "$backup_file"; then
      echo "$(date -Iseconds) - 备份 $rules_file 到 $backup_file, 大小 $(du -h $backup_file | awk '{print $1}')" && rm -f "$rules_file"
      # 删除原始规则文件
      # rm -f "$rules_file"
      echo "$(date -Iseconds) - 删除 $rules_file 文件"
      echo
    else
      echo "$(date -Iseconds) - 备份 $rules_file 失败"
      exit 1
    fi
  fi

  if [[ ${disk_arr[0]:0:4} == "/dev" ]]; then
    # 标记磁盘为/dev类型
    local is_dev_disk=true
  else
    local is_dev_disk=false
  fi
  if [[ $multipath = "Y" && $is_dev_disk = true ]]; then
    # 如果磁盘是/dev类型且需要使用多路径，则标记磁盘为多路径类型
    local is_multipath=true
  else
    local is_multipath=false
  fi

  if $is_multipath; then
    # 安装device-mapper-multipath软件包
    yum install -q -y device-mapper-multipath --skip-broken
    mpathconf --enable --with_multipathd y
  fi
  if [[ "$node_num" = 1 && "$is_multipath" = true ]]; then
    cat <<EOF >"$MULTIPATH_CONF_PATH"
$DEFAULT_MULTIPATH_CONF
  
blacklist {
  devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*"
  devnode "^(hd|xvd|vd)[a-z]*"
}
  
multipaths {
EOF
  fi

  # 定义创建ASM磁盘数组函数
  asm_disk_arr=(dmdcr dmvote ${log_disk:+dmlog} ${arch_disk:+dmarch} dmdata)

  my_dsc_conf_asmdisk() {
    for ((x = 0; x < ${#disk_arr[@]}; x++)); do
      y=1
      for i in ${disk_arr[$x]//,/ }; do
        if ((y < 10)); then
          dsc_conf_asmdisk "$(lower "$i")" "${asm_disk_arr[$x]}"0$y
        else
          dsc_conf_asmdisk "$(lower "$i")" "${asm_disk_arr[$x]}"$y
        fi
        y=$((y + 1))
      done
    done
  }
  # 打印multipath.conf和udev.rules规则文件
  if [ "$node_num" -eq 1 ]; then
    if $is_multipath; then
      execute_with_retry "my_dsc_conf_asmdisk" "3"                                                                                              # 配置asmdisk
      printf "}\n\ndevices {\n  device {\n    path_grouping_policy  group_by_prio\n    prio alua  #failover\n  }\n}\n" >>"$MULTIPATH_CONF_PATH" # 在multipath配置文件中添加路径分组策略和优先级
      printf "\e[34m文件 %s 的内容为：\e[0m\n\n%s\n\n" "$MULTIPATH_CONF_PATH" "$(cat $MULTIPATH_CONF_PATH)"                                            # 打印multipath配置文件的内容
    else
      execute_with_retry "my_dsc_conf_asmdisk" "3" # 配置asmdisk
    fi
    printf "\e[34m文件 %s 的内容为：\e[0m\n\n%s\n" "$rules_file" "$(cat $rules_file)"
  else
    if $is_multipath; then
      scp -q "${wc_pubip_arr[0]}:$MULTIPATH_CONF_PATH" "$MULTIPATH_CONF_PATH"                          # 从第一个节点复制multipath配置文件到本地
      printf "\n\e[34m文件 %s 的内容为：\e[0m\n\n%s\n\n" "$MULTIPATH_CONF_PATH" "$(cat $MULTIPATH_CONF_PATH)" # 打印multipath配置文件的内容
      # 检查是否存在属于 dmdba 或 dinstall 的现有设备
      if find /dev/ -maxdepth 1 \( -user "dmdba" -or -group "dinstall" \) >/dev/null 2>&1; then
        scp -q "${wc_pubip_arr[0]}:$rules_file" "$rules_file" # 从第一个节点复制udev规则文件到本地
      elif [[ ! -d $ASM_DEVICE_PATH || $(find $ASM_DEVICE_PATH -mindepth 1 -name 'dm*' | wc -l) -eq 0 ]]; then
        scp -q "${wc_pubip_arr[0]}:$rules_file" "$rules_file" # 从第一个节点复制udev规则文件到本地
      fi
    else
      scp -q "${wc_pubip_arr[0]}:$rules_file" "$rules_file" # 从第一个节点复制udev规则文件到本地
    fi
    printf "\e[34m文件 %s 的内容为：\e[0m\n\n%s\n" "$rules_file" "$(cat $rules_file)" # 打印udev规则文件的内容
  fi
  dsc_conf_udev
}

#==============================================================#
#                    配置dmdcr_cfg.ini                         #
#==============================================================#
function dsc_dmdcr_cfg() {
  local cfg_path="$data_dir/$dsc_db_name/dmdcr_cfg.ini"
  local ep_count=${#wc_malip_arr[@]}

  write_cfg_endpoints() {
    cat <<-CONFIG >"$cfg_path"
DCR_N_GRP = 3
DCR_VTD_PATH = $ASM_DEVICE_PATH/${asm_disk_arr[1]}01
DCR_OGUID = $oguid
CONFIG
  }

  # 存储节点配置信息的关联数组
  declare -A groups=(
    ["CSS"]="DCR_GRP_TYPE = CSS
DCR_GRP_NAME = GRP_CSS
DCR_GRP_N_EP = $ep_count
DCR_GRP_DSKCHK_CNT = 60"
    ["ASM"]="DCR_GRP_TYPE = ASM
DCR_GRP_NAME = GRP_ASM
DCR_GRP_N_EP = $ep_count
DCR_GRP_DSKCHK_CNT = 60"
    ["DB"]="DCR_GRP_TYPE = DB
DCR_GRP_NAME = GRP_$dsc_db_name
DCR_GRP_N_EP = $ep_count
DCR_GRP_DSKCHK_CNT = 60"
  )

  # 内嵌函数：写入CSS节点的端点信息
  write_css_endpoints() {
    # 循环遍历CSS节点并写入文件
    for ((x = 0; x < ${#wc_malip_arr[@]}; x++)); do
      cat <<-CONFIG >>"$cfg_path"
[GRP_CSS]
DCR_EP_NAME = CSS$x 
DCR_EP_HOST = ${wc_malip_arr[$x]}
DCR_EP_PORT = $css_dcr_ep_port
CONFIG
    done
  }

  # 内嵌函数：写入ASM节点的端点信息
  write_asm_endpoints() {
    # 循环遍历ASM节点并写入文件
    for ((x = 0; x < ${#wc_malip_arr[@]}; x++)); do
      cat <<-CONFIG >>"$cfg_path"
[GRP_ASM]
DCR_EP_NAME = ASM$x
DCR_EP_SHM_KEY = 9336$x
DCR_EP_SHM_SIZE = 512
DCR_EP_HOST = ${wc_malip_arr[$x]}
DCR_EP_PORT = $asm_dcr_ep_port
DCR_EP_ASM_LOAD_PATH = $ASM_DEVICE_PATH
CONFIG
    done
  }

  # 内嵌函数：写入DB节点的端点信息
  write_db_endpoints() {
    # 循环遍历DB节点并写入文件
    for ((x = 0; x < ${#wc_malip_arr[@]}; x++)); do
      cat <<-CONFIG >>"$cfg_path"
[GRP_$dsc_db_name]
DCR_EP_NAME = $(upper "${dsc_ep_name}")$x
DCR_EP_SEQNO = $x
DCR_EP_PORT = $port_num
DCR_CHECK_PORT = $db_dcr_ep_port
CONFIG
    done
  }

  # 内嵌函数：创建新的配置文件
  create_config_file() {
    # 配置dmdcr_cfg.ini参数
    write_cfg_endpoints
    # 写入端点配置
    # 按照 CSS ASM DB 顺序遍历每个组并写入文件
    for group in CSS ASM DB; do
      # 如果当前组存在，则写入组信息和端点信息
      if [[ "${groups[$group]+exists}" ]]; then
        cat <<-CONFIG >>"$cfg_path"

[GRP]
${groups[$group]}
CONFIG
        # 根据当前组写入端点信息
        case "$group" in
        "CSS") write_css_endpoints ;; # 写入 CSS 组的端点信息
        "ASM") write_asm_endpoints ;; # 写入 ASM 组的端点信息
        "DB") write_db_endpoints ;;   # 写入 DB 组的端点信息
        esac
      fi
    done
  }
  # 内嵌函数：复制现有的配置文件
  copy_config_file() {
    log_print "正在复制用于DSC的参数文件 $dsc_db_name" ""
    # 复制文件到本地目录并更改所有权
    scp "${wc_pubip_arr[0]}:$cfg_path" "$cfg_path" >/dev/null 2>&1
  }

  # 主逻辑
  if [[ $node_num = 1 ]]; then
    log_print "正在创建用于DSC的参数文件 $dsc_db_name" ""
    # 创建新的配置文件
    create_config_file
  else
    # 复制现有的配置文件
    copy_config_file
  fi
  chown -R dmdba.dinstall "$cfg_path"
}

#==============================================================#
#                   配置dmasmvrmal.ini                         #
#==============================================================#
function dsc_dmasvmal() {
  local dmasvmal_path="$data_dir/$dsc_db_name/dmasvrmal.ini"

  # 检查dmasvrmal.ini文件是否存在，如果存在则删除
  if [ -f "$dmasvmal_path" ]; then
    rm "$dmasvmal_path"
  fi

  # 如果节点是集群中的第一个节点
  if [[ $node_num = 1 ]]; then
    # 循环遍历wc_malip_arr数组，并为每个元素创建一个[MAL_INST]部分
    for ((x = 0; x < ${#wc_malip_arr[@]}; x++)); do
      # 将[MAL_INST]部分追加到dmasvrmal.ini文件中
      cat <<-EOF >>"$dmasvmal_path"
[MAL_INST$((x + 1))] 
   MAL_INST_NAME              = ASM$x
   MAL_HOST                   = ${wc_malip_arr[$x]}
   MAL_PORT                   = $asvrmal_mal_port
  
EOF
    done
  # 如果节点不是集群中的第一个节点
  else
    # 将dmasvrmal.ini文件从第一个节点的数据目录复制到当前节点的数据目录
    scp "${wc_pubip_arr[0]}":"$dmasvmal_path" "$dmasvmal_path" >/dev/null 2>&1
  fi
  # 将dmasvrmal.ini文件的所有权更改为dmdba.dinstall
  chown dmdba.dinstall "$dmasvmal_path"
}

#==============================================================#
#                    配置dmdcr.ini                             #
#==============================================================#
#参数DMDCR_SEQNO不同节点值不一样，从0开始依次增加
function dsc_dmdcr() {
  local dmdcr_path="$data_dir/$dsc_db_name/dmdcr.ini"
  cat <<-EOF >"$dmdcr_path"
DMDCR_PATH                 = $ASM_DEVICE_PATH/${asm_disk_arr[0]}01
DMDCR_MAL_PATH             = $data_dir/$dsc_db_name/dmasvrmal.ini
DMDCR_SEQNO                = $((node_num - 1)) 
DMDCR_ASM_RESTART_INTERVAL = 0
DMDCR_ASM_STARTUP_CMD      = $env_dm_home/bin/dmasmsvr dcr_ini=$data_dir/$dsc_db_name/dmdcr.ini
DMDCR_DB_RESTART_INTERVAL  = 0
DMDCR_DB_STARTUP_CMD       = $env_dm_home/bin/dmserver path=$data_dir/$dsc_db_name/$dsc_ep_name$((node_num - 1))_config/dm.ini dcr_ini=$data_dir/$dsc_db_name/dmdcr.ini
EOF
  chown dmdba.dinstall "$dmdcr_path"
}

#==============================================================#
#                   数据库监控工具
#==============================================================#
# 生成dmcssm.ini配置文件
function dsc_dmcssm() {
  local dmcssm_path="$data_dir/$dsc_db_name/dmcssm.ini"
  cat <<-EOF >"$dmcssm_path"
CSSM_OGUID = $oguid
$(echo "${wc_malip_arr[@]}" | awk -v port="$css_dcr_ep_port" '{for(i=1;i<=NF;i++) printf "CSSM_CSS_IP = %s:%s\n", $i, port}')
CSSM_LOG_PATH = $env_dm_home/log
CSSM_LOG_FILE_SIZE = 32
CSSM_LOG_SPACE_LIMIT = 0 
EOF
  chown dmdba.dinstall "$dmcssm_path"
  ls -lh "$data_dir/$dsc_db_name"
}

#==============================================================#
#                   数据库连接服务名
#==============================================================#
function dsc_dmsvc() {

  # 使用 Here Document 将配置设置追加到 /etc/dm_svc.conf 文件中
  su - dmdba <<-EOF
    cat >> /etc/dm_svc.conf <<CONF

[$dsc_db_name]
$dsc_db_name=($(printf "%s:$port_num," "${wc_pubip_arr[@]}" | sed 's/,$//')) 
SWITCH_TIMES=(60)
SWITCH_INTERVAL=(1000)
EP_SELECTOR=(0)
AUTO_RECONNECT=(1)
CONF
EOF

}

#==============================================================#
#                    初始化磁盘                                #
#==============================================================#
#asm_disk_arr
function dsc_dmasmcmd() {
  local asm_disk_file="$data_dir/$dsc_db_name/asmdisk.txt"

  log_print "初始化 ASM 磁盘" ""

  # 删除现有的 asm 磁盘文件（如果有）
  rm -f "$asm_disk_file"

  # 使用 Here Document 创建 asm 磁盘文件
  cat <<-EOF >"$data_dir"/"$dsc_db_name"/asmdisk.txt
#asm script file
create dcrdisk '/dev/asmdisk/${asm_disk_arr[0]}01' '${asm_disk_arr[0]}01'
create votedisk '/dev/asmdisk/${asm_disk_arr[1]}01' '${asm_disk_arr[1]}01' 
EOF
  # 创建共享磁盘
  for ((x = 2; x < ${#disk_arr[@]}; x++)); do
    y=1
    for i in ${disk_arr[$x]//,/ }; do
      asm_disk_name=${asm_disk_arr[$x]}$(printf '%02d' $y)
      asm_disk_path="/dev/asmdisk/$asm_disk_name"
      cat <<-EOF >>"$data_dir"/"$dsc_db_name"/asmdisk.txt
create asmdisk '$asm_disk_path' '$asm_disk_name' 
EOF
      y=$((y + 1))
    done
  done
  cat <<-EOF >>"$data_dir"/"$dsc_db_name"/asmdisk.txt
init dcrdisk '/dev/asmdisk/${asm_disk_arr[0]}01' from '$data_dir/$dsc_db_name/dmdcr_cfg.ini'identified by '$asm_pwd' 
init votedisk '/dev/asmdisk/${asm_disk_arr[1]}01' from '$data_dir/$dsc_db_name/dmdcr_cfg.ini' 
EOF

  # 以 dmdba 用户身份运行 asm 磁盘创建脚本
  chown dmdba.dinstall "$asm_disk_file"
  su - dmdba -c "$env_dm_home/bin/dmasmcmd script_file=$asm_disk_file"
}

#==============================================================#
#                    注册服务
#==============================================================#
function dsc_dmservice() {
  local DM_CSS_SERVICE="DmCSSServiceCss"
  local DM_ASM_SERVICE="DmASMSvrServiceAsmsvr"
  local SYSTEMD_PATH="/usr/lib/systemd/system/"

  log_print "注册css和asm服务" ""

  # 删除残余服务脚本
  if [[ $os_version != "6" ]]; then
    rm -f "$SYSTEMD_PATH$DM_CSS_SERVICE.service" || true
    rm -f "$SYSTEMD_PATH$DM_ASM_SERVICE.service" || true
  fi

  # 安装 CSS 和 ASMSVR 服务
  local DM_SERVICE_INSTALLER="$env_dm_home/script/root/dm_service_installer.sh"
  local DCR_INI="$data_dir/$dsc_db_name/dmdcr.ini"
  $DM_SERVICE_INSTALLER -t dmcss -dcr_ini "$DCR_INI" -p Css
  echo
  $DM_SERVICE_INSTALLER -t dmasmsvr -dcr_ini "$DCR_INI" -p Asmsvr -y $DM_CSS_SERVICE

  if [[ $os_version = "6" ]]; then
    # 创建符号链接和修改权限
    ln -sf "/etc/rc.d/init.d/$DM_CSS_SERVICE" "$env_dm_home/bin/$DM_CSS_SERVICE"
    chmod 755 "$env_dm_home/bin/$DM_CSS_SERVICE"
    ln -sf "/etc/rc.d/init.d/$DM_ASM_SERVICE" "$env_dm_home/bin/$DM_ASM_SERVICE"
    chmod 755 "$env_dm_home/bin/$DM_ASM_SERVICE"
  fi
}
#==============================================================#
#                    循环启动所有的节点的css和asm实例的服务
#==============================================================#
function dsc_start_ca() {
  log_print "启动css和asm服务" ""

  local CSS_START_CMD="$env_dm_home/bin/DmCSSServiceCss start"          # CSS服务启动命令
  local ASMSVR_START_CMD="$env_dm_home/bin/DmASMSvrServiceAsmsvr start" # ASM实例服务启动命令
  local SSH_CMD_TEMPLATE="su - dmdba -c"                                # 切换到dmdba用户并执行命令的命令模板
  local WAIT_TIME="5s"                                                  # 等待时间

  ##依次启动每个节点的css服务
  for ((x = 0; x < ${#wc_pubip_arr[@]}; x++)); do
    while true; do
      sleep "${WAIT_TIME}"
      ssh "${wc_pubip_arr[$x]}" "${SSH_CMD_TEMPLATE} \"${CSS_START_CMD}\""
      if [ $? -eq 0 ]; then
        echo
        color_printf blue "节点 ${wc_pubip_arr[$x]} 启动成功"
        echo
        break
      fi
    done
  done

  ##依次启动每个节点的asm实例服务
  for ((x = 0; x < ${#wc_pubip_arr[@]}; x++)); do
    while true; do
      sleep "${WAIT_TIME}"
      ssh "${wc_pubip_arr[$x]}" "${SSH_CMD_TEMPLATE} \"${ASMSVR_START_CMD}\""
      if [ $? -eq 0 ]; then
        echo
        color_printf blue "节点 ${wc_pubip_arr[$x]} 启动成功"
        echo
        break
      fi
    done
  done
}
#==============================================================#
#                    创建ASM磁盘组
#==============================================================#
function dsc_dmasmtool() {
  log_print "创建ASM磁盘组" ""
  local asm_disk_prefix
  local dmasmtool_cmd
  local exit_code
  local sleep_time=5s

  for ((x = 2; x < ${#disk_arr[@]}; x++)); do
    asm_disk_prefix="${asm_disk_arr[$x]}"
    dmasmtool_cmd="su - dmdba -c \"$env_dm_home/bin/dmasmtool DCR_INI=$data_dir/$dsc_db_name/dmdcr.ini\""
    y=1
    for i in ${disk_arr[$x]//,/ }; do
      if [ $y -eq 1 ]; then
        dmasmtool_cmd+=" <<-DMASMTOOL
create diskgroup '$(upper "$asm_disk_prefix")' asmdisk '$ASM_DEVICE_PATH/${asm_disk_prefix}0$y'
exit
DMASMTOOL"
      else
        dmasmtool_cmd+=" <<-DMASMTOOL
alter diskgroup '$(upper "$asm_disk_prefix")' add asmdisk '$ASM_DEVICE_PATH/${asm_disk_prefix}$(printf %02d $y)'
exit
DMASMTOOL"
      fi
      y=$((y + 1))
    done
    while true; do
      sleep "$sleep_time"
      eval "$dmasmtool_cmd"
      exit_code=$?
      if [ $exit_code -eq 0 ]; then
        break
      fi
    done
  done
  echo
}

#==============================================================#
#                    初始化数据库
#==============================================================#
function init_dsc_db() {

  log_print "初始化数据库实例" ""

  local init_dsc_db_file="$data_dir/$dsc_db_name/dminit.ini"

  lastIndex=$((${#asm_disk_arr[@]} - 1))

  # 定义一个关联数组，包含各个数据库文件的路径和大小
  declare -A paths_and_sizes=(
    [system_path]="+$(upper "${asm_disk_arr[$lastIndex]}")/dmdata"                    # 系统文件路径
    [system]="+$(upper "${asm_disk_arr[$lastIndex]}")/dmdata/$dsc_db_name/system.dbf" # 系统文件路径和名称
    [system_size]="128"                                                               # 系统文件大小
    [roll]="+$(upper "${asm_disk_arr[$lastIndex]}")/dmdata/$dsc_db_name/roll.dbf"     # 回滚文件路径和名称
    [roll_size]="128"                                                                 # 回滚文件大小
    [main]="+$(upper "${asm_disk_arr[$lastIndex]}")/dmdata/$dsc_db_name/main.dbf"     # 主文件路径和名称
    [main_size]="128"                                                                 # 主文件大小
    [ctl_path]="+$(upper "${asm_disk_arr[$lastIndex]}")/dmdata/$dsc_db_name/dm.ctl"   # 控制文件路径和名称
    [ctl_size]="8"                                                                    # 控制文件大小
    [log_size]="$log_size"                                                            # 日志文件大小
    [dcr_path]="$ASM_DEVICE_PATH/${asm_disk_arr[0]}01"                                # ASM磁盘路径
    [dcr_seqno]="0"                                                                   # ASM磁盘序列号
    [auto_overwrite]="2"                                                              # 自动覆盖策略
    [EXTENT_SIZE]="$extent_size"                                                      # 区大小
    [PAGE_SIZE]="$page_size"                                                          # 页大小
    [CASE_SENSITIVE]="$case_sensitive"                                                # 是否大小写敏感
    [CHARSET]="$charset"                                                              # 字符集
    [LENGTH_IN_CHAR]="$length_in_char"                                                # 字符长度
    [LOG_SIZE]="$log_size"                                                            # 日志文件大小
  )

  # 将路径和大小写入配置文件dminit.ini
  cat >"$init_dsc_db_file" <<EOF
db_name = $dsc_db_name
$(for key in "${!paths_and_sizes[@]}"; do
    printf '%s = %s\n' "$key" "${paths_and_sizes[$key]}"
  done)
EOF

  # 针对每个endpoint写入配置信息到dminit.ini
  for x in "${!wc_malip_arr[@]}"; do
    if [[ -z "$log_disk" ]]; then
      cat >>"$init_dsc_db_file" <<EOF
[$(upper "${dsc_ep_name}")$x]
config_path = $data_dir/$dsc_db_name/$dsc_ep_name${x}_config
port_num = $port_num
mal_host = ${wc_pubip_arr[$x]}
mal_port = 9340
log_path = +$(upper "${asm_disk_arr[$lastIndex]}")/dmdata/$dsc_db_name/$dsc_ep_name${x}_log01.log
log_path = +$(upper "${asm_disk_arr[$lastIndex]}")/dmdata/$dsc_db_name/$dsc_ep_name${x}_log02.log
EOF
    else
      cat >>"$init_dsc_db_file" <<EOF
[$(upper "${dsc_ep_name}")$x]
config_path = $data_dir/$dsc_db_name/$dsc_ep_name${x}_config
port_num = $port_num
mal_host = ${wc_pubip_arr[$x]}
mal_port = 9340
log_path = +$(upper "${asm_disk_arr[2]}")/dmlog/$dsc_db_name/$dsc_ep_name${x}/$dsc_ep_name${x}_log01.log
log_path = +$(upper "${asm_disk_arr[2]}")/dmlog/$dsc_db_name/$dsc_ep_name${x}/$dsc_ep_name${x}_log02.log
EOF
    fi
  done

  chown dmdba.dinstall "$init_dsc_db_file"
  # 使用dminit命令初始化数据库
  su - dmdba -c "$env_dm_home/bin/dminit control=$init_dsc_db_file"

  printf '\n\033[34m%s\033[0m\n' '数据库初始化完成'
}

#==============================================================#
#                         启动服务
#==============================================================#
function dsc_start() {
  log_print "注册服务并启动数据库" ""

  cat <<EOF >"$software_dir/starttmp.sh"
#!/bin/bash
# 删除软链接（如果存在）
if [[ -L "$env_dm_home/bin/DmService$dsc_db_name" ]]; then
  rm -f "$env_dm_home/bin/DmService$dsc_db_name"
fi

# 删除服务文件（如果存在）
if [[ $os_version != "6" ]] && [[ -e /usr/lib/systemd/system/DmService${dsc_db_name}.service ]]; then
  rm -f /usr/lib/systemd/system/DmService"${dsc_db_name}".service
fi

# 安装服务，并使用指定的参数配置
$env_dm_home/script/root/dm_service_installer.sh \
-t dmserver \
-dm_ini "$data_dir/$dsc_db_name/$dsc_ep_name$((node_num - 1))_config/dm.ini" \
-dcr_ini "$data_dir/$dsc_db_name/dmdcr.ini" \
-p "$dsc_db_name" \
-y DmASMSvrServiceAsmsvr
echo
# 创建软链接（如果操作系统版本为6）
if [[ $os_version = "6" ]]; then
  ln -sf "/etc/rc.d/init.d/DmService$dsc_db_name" "$env_dm_home/bin/DmService$dsc_db_name"
  chmod 755 "$env_dm_home/bin/DmService$dsc_db_name"
fi

# 等待服务启动
while ! su - dmdba -c "$env_dm_home/bin/DmService$dsc_db_name start"; do
  sleep 5s
done
EOF

  # SSH连接命令
  ssh_cmd="ssh -q -t"

  # 执行远程命令
  function exec_remote_cmd {
    local node="$1"
    local cmd="$2"
    $ssh_cmd "${node}" "${cmd}"
  }

  # 打印输出信息
  function print_output {
    local color="$1"
    local msg="$2"
    color_printf "${color}" "${msg}"
    echo
  }

  # 修改数据库参数
  function modify_db_params {
    local node="$1"
    exec_remote_cmd "${node}" "sed -i '4s/0/180/g' ${data_dir}/${dsc_db_name}/dmdcr.ini"
    exec_remote_cmd "${node}" "sed -i '6s/0/360/g' ${data_dir}/${dsc_db_name}/dmdcr.ini"
  }

  # 创建归档
  function create_archive {
    local node="$1"
    local number="$2"
    echo -e "\n$(print_output blue "在节点 ${node} 创建归档")\n"
    exec_remote_cmd "${node}" "su - dmdba -c \"disql -S SYSDBA/SYSDBA:${port_num} '\\\`/home/dmdba/scripts/conf_arch.sql'\""
    if [[ $is_local_arch = "Y" && $arch_disk = "" ]]; then
      exec_remote_cmd "${node}" "sed -i '4i ARCH_LOCAL_SHARE = 0\n' ${data_dir}/${dsc_db_name}/${dsc_ep_name}${number}_config/dmarch.ini"
    else
      exec_remote_cmd "${node}" "sed -i '4i ARCH_LOCAL_SHARE = 1\n' ${data_dir}/${dsc_db_name}/${dsc_ep_name}${number}_config/dmarch.ini"
      exec_remote_cmd "${node}" "sed -i '6i ARCH_LOCAL_SHARE_CHECK = 0\n' ${data_dir}/${dsc_db_name}/${dsc_ep_name}${number}_config/dmarch.ini"
    fi
  }

  # 注册服务和启动数据库
  lastIndex=$((${#wc_pubip_arr[@]} - 1))
  for ((x = 0; x < ${#wc_pubip_arr[@]}; x++)); do
    if ((x > 0)); then
      scp -r "${data_dir}"/"${dsc_db_name}"/"${dsc_ep_name}"${x}_config "${wc_pubip_arr[$x]}":"${data_dir}"/"${dsc_db_name}"/ >/dev/null 2>&1
      exec_remote_cmd "${wc_pubip_arr[$x]}" "chown -R dmdba.dinstall ${data_dir}/${dsc_db_name}/${dsc_ep_name}${x}_config"
      scp -r "${software_dir}"/starttmp.sh "${wc_pubip_arr[$x]}":"${software_dir}"/starttmp.sh >/dev/null 2>&1
      exec_remote_cmd "${wc_pubip_arr[$x]}" "sed -i '13s/'$((node_num - 1))'/'$x'/g' ${software_dir}/starttmp.sh"
    fi

    # 注册服务和启动数据库
    print_output blue "在节点 ${wc_pubip_arr[$x]} 注册服务并启动数据库"
    exec_remote_cmd "${wc_pubip_arr[$x]}" "bash ${software_dir}/starttmp.sh"
    print_output blue "节点 ${wc_pubip_arr[$x]} 启动成功"

    # 修改数据库参数
    modify_db_params "${wc_pubip_arr[$x]}"

    # 创建归档
    if ((x == lastIndex)); then
      log_print "创建数据库归档" ""
      # 测试数据库是否正常打开
      while true; do
        PID=$(pgrep -f "$data_dir"/"$dsc_db_name"/"$dsc_ep_name"0_config/dm.ini)
        if [ -n "$PID" ]; then
          connResult=$(su - dmdba -c "disql -S -L SYSDBA/SYSDBA:$port_num -c \"set heading off\" -e 'select 1'" | tail -n 1)
          if [[ $connResult = 1 ]]; then
            break
          fi
        fi
        sleep 0.5s
      done
      for ((y = 0; y < ${#wc_pubip_arr[@]}; y++)); do
        create_archive "${wc_pubip_arr[$y]}" "$y"
      done
      optimize_dmdb
    fi
  done

}

#==============================================================#
#                    部署数据库
#==============================================================#
## 判断必要参数和生成oguid值
function install_wc_oguid() {
  if [[ $dm_install_mode = "datawatch" && (! $root_passwd || ! $wc_public_ip || ! $wc_mal_ip) ]]; then
    color_printf red "数据守护集群安装模式必须设置以下参数: -rp, -dpi, -dmi. 请键入 'sh DMShellInstall --help' 获取更多帮助！"
    exit 99
  elif [[ $dm_install_mode = "dsc" && (! $root_passwd || ! $wc_public_ip || ! $wc_mal_ip || ! $dcr_disk || ! $vote_disk || ! $data_disk) ]]; then
    color_printf red "DSC集群安装模式必须设置以下参数: -rp, -dpi, -dmi, -dcd, -ard, -dad. 请键入 'sh DMShellInstall --help' 获取更多帮助！"
    exit 99
  fi
  # 生成全局oguid
  oguid=$(rand 400000 500000)
}

##配置特殊参数
function mode_wc_para() {
  local params=""

  [[ $hostname ]] && params+=" -hn $hostname"
  [[ $dm_iso_name ]] && params+=" -di $dm_iso_name"
  [[ $timeserver_ip ]] && params+=" -ti $timeserver_ip"
  [[ $key_path ]] && params+=" -kp $key_path"

  if [[ $dm_install_mode = "dsc" ]]; then
    [[ $log_disk ]] && params+=" -lod $log_disk"
    [[ $arch_disk ]] && params+=" -ard $arch_disk"
  fi

  echo -n "$params" >>"$software_dir"/dwnode.sh
}

##分发其他节点
function send_other_node() {
  # 循环遍历除第一个元素外的每个IP地址，并在其上执行以下操作：
  for ((i = 1; i < ${#wc_pubip_arr[@]}; i++)); do
    echo
    color_printf blue "拷贝脚本以及安装包到节点：${wc_pubip_arr[$i]}"
    echo

    # 在目标服务器上，如果指定路径下存在文件或目录，则删除它并创建一个新目录。
    ssh "${wc_pubip_arr[$i]}" "[[ -f $software_dir || -d $software_dir ]] && rm -rf $software_dir ; mkdir -p $software_dir"

    # 将本地文件夹中的dwnode.sh复制到目标节点的相应路径中，
    # 并在该文件中追加"-node $((i + 1))"字符串。
    scp "$software_dir"/dwnode.sh "${wc_pubip_arr[$i]}":"$software_dir"
    ssh "${wc_pubip_arr[$i]}" "echo -n \" -node $((i + 1))\" >>$software_dir/dwnode.sh"

    # 将本地文件夹中的DMShellInstall文件以及可能的iso文件复制到目标节点的相应路径中。
    scp "$software_dir"/DMShellInstall "${wc_pubip_arr[$i]}":"$software_dir"
    if [[ $dm_iso_name ]]; then
      scp "$software_dir"/"$dm_iso_name" "${wc_pubip_arr[$i]}":"$software_dir"
    else
      scp "$software_dir"/DMInstall.bin "${wc_pubip_arr[$i]}":"$software_dir"
    fi
  done
  echo
}
##安装数据守护
function install_dw() {

  # 数据守护实例名
  dw_instance_name="${dw_group_name}"_DW_0
  instance_name="${dw_instance_name}${node_num}"

  # 配置互信
  if [[ "${node_num}" -eq 1 ]]; then
    install_wc_oguid

    # 检查是否需要配置监视器的互信
    if [[ -n "${dw_monitor_ip}" && "$(ssh_check root "${wc_public_ip}","${dw_monitor_ip}")" = "false" ]]; then
      ssh_trust root "${root_passwd}" "${wc_public_ip}","${dw_monitor_ip}"
    elif [[ "$(ssh_check root "${wc_public_ip}")" = "false" ]]; then
      ssh_trust root "${root_passwd}" "${wc_public_ip}"
    fi

    # 创建安装脚本
    local dw_install_script="${software_dir}/dwnode.sh"
    echo -n "sh ${software_dir}/DMShellInstall -dp '${dm_passwd}' -d ${env_dm_home} -dd ${data_dir} -ad ${arch_dir} -bd ${backup_dir} -es ${extent_size} -ps ${page_size} -cs ${case_sensitive} -c ${charset} -lic ${length_in_char} -ls ${log_size} -sl ${space_limit} -pn ${port_num} -sp '${sysdba_pwd}' -bm ${backup_mode} -m ${only_conf_os} -ud ${install_until_db} -oid ${only_init_db} -opd ${optimize_db} -oopd ${only_optimize_db} -mp ${mem_percent} -sm ${sort_mode} -dpi ${wc_public_ip} -dmi ${wc_mal_ip} -rp '${root_passwd}' -dgn ${dw_group_name} -mpn ${mal_port_num} -mdpn ${mal_dw_port_num} -midpn ${mal_inst_dw_port_num} -oguid ${oguid} -dim ${dm_install_mode} -oi ${os_iso}" >"${dw_install_script}"

    # 配置特殊参数
    mode_wc_para

    # 拷贝提示
    log_print "拷贝脚本以及安装包到部署节点:" ""

    # 分发到监视器节点
    if [[ -n "${dw_monitor_ip}" ]]; then
      # 将监视器 IP 传输到备库节点
      echo -n " -dmoi ${dw_monitor_ip}" >>"${dw_install_script}"

      # 创建监视器节点安装脚本
      local dw_monitor_install_script="${software_dir}/dwmonitor_node.sh"
      echo -n "sh ${software_dir}/DMShellInstall -dp '${dm_passwd}' -d ${env_dm_home} -dd ${data_dir} -ad ${arch_dir} -bd ${backup_dir} -ud Y -dpi ${wc_public_ip} -dmi ${wc_mal_ip} -rp '${root_passwd}' -dgn ${dw_group_name} -mdpn ${mal_dw_port_num} -oguid ${oguid} -dim ${dm_install_mode} -oi ${os_iso}" >"${dw_monitor_install_script}"

      # 配置特殊参数
      if [[ -n "${dm_iso_name}" ]]; then
        echo -n " -di ${dm_iso_name}" >>"${dw_monitor_install_script}"
      fi
      if [[ -n "${timeserver_ip}" ]]; then
        echo -n " -ti ${timeserver_ip}" >>"${dw_monitor_install_script}"
      fi
      if [[ -n "${key_path}" ]]; then
        echo -n " -kp ${key_path}" >>"${dw_monitor_install_script}"
      fi

      # 拷贝脚本以及安装包到监视器节点
      color_printf blue "拷贝脚本以及安装包到监视器节点 ${dw_monitor_ip}"
      ssh "${dw_monitor_ip}" "rm -rf ${software_dir} ; mkdir -p ${software_dir}"
      scp "${dw_monitor_install_script}" "${dw_monitor_ip}:${software_dir}"
      ssh "${dw_monitor_ip}" "echo -n ' -node -1' >>${dw_monitor_install_script}"
      scp "${software_dir}/DMShellInstall" "${dw_monitor_ip}:${software_dir}"
      if [[ -n "${dm_iso_name}" ]]; then
        scp "${software_dir}/${dm_iso_name}" "${dw_monitor_ip}:${software_dir}"
      else
        scp "${software_dir}/DMInstall.bin" "${dw_monitor_ip}:${software_dir}"
      fi
      echo
    fi

    # 分发节点
    send_other_node
  fi
}

##安装dsc
function install_dsc() {
  install_wc_oguid
  ssh_trust root "$root_passwd" "$wc_public_ip"
  echo -n "sh $software_dir/DMShellInstall -dp $dm_passwd -d $env_dm_home -dd $data_dir -ad $arch_dir -bd $backup_dir -ddn $dsc_db_name -den $dsc_ep_name -es $extent_size -ps $page_size -cs $case_sensitive -c $charset -lic $length_in_char -ls $log_size -sl $space_limit -pn $port_num -sp '$sysdba_pwd' -apd '$asm_pwd' -bm $backup_mode -m $only_conf_os -ud $install_until_db -oid $only_init_db -opd $optimize_db -oopd $only_optimize_db -mp $mem_percent -sm $sort_mode -dpi $wc_public_ip -dmi $wc_mal_ip -rp '$root_passwd' -fd $format_disk -dcd $dcr_disk -vod $vote_disk -dad $data_disk -ila $is_local_arch -cdp $css_dcr_ep_port -adp $asm_dcr_ep_port -ddp $db_dcr_ep_port -amp $asvrmal_mal_port -oguid $oguid -dim $dm_install_mode -mtp $multipath -oi $os_iso" >"$software_dir"/dwnode.sh
  ## 配置特殊参数
  mode_wc_para
  ## 分发节点
  send_other_node
}

#==============================================================#
#                            程序入口                           #
#==============================================================#
function main() {
  ## 检查是否 root 用户执行，否则退出
  if [[ $USER != "root" ]]; then
    color_printf red "脚本必须使用root用户执行，请切换到root用户!"
    exit 99
  fi

  ## 检查当前目录是否是root根目录以及的/dm目录，否则退出
  if [[ $software_dir = "/root" || $software_dir = "$env_dm_home" ]]; then
    color_printf red "达梦软件包以及脚本位置不能是/root或者$env_dm_home!"
    exit 99
  fi

  ## 脚本参数处理
  while [[ $1 ]]; do
    case $1 in
    -node | --node_num)
      node_num=$2
      shift 2
      ;;
    -oi | --os_iso)
      if [[ $2 ]]; then
        os_iso=$2
        if [[ $os_iso != "N" && $os_iso != "Y" ]]; then
          color_printf red "请检查参数 [-oi, --os_iso] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-oi, --os_iso] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -hn | --hostname)
      if [[ $2 ]]; then
        hostname=$2
      else
        color_printf red "请检查参数 [-hn, --hostname] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -rp | --root_passwd)
      if [[ $2 ]]; then
        root_passwd=$2
      else
        color_printf red "请检查参数 [-rp, --root_passwd] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dp | --dm_passwd)
      if [[ $2 ]]; then
        dm_passwd=$2
      else
        color_printf red "请检查参数 [-dp, --dm_passwd] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dpi | --wc_public_ip)
      if [[ $2 ]]; then
        wc_public_ip=$2
        ## 业务 IP 地址拆分
        for i in ${wc_public_ip//,/ }; do
          ## 将业务IP加入数组中
          wc_pubip_arr+=("$i")
        done
      else
        color_printf red "请检查参数 [-dpi, --wc_public_ip] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dmi | --wc_mal_ip)
      if [[ $2 ]]; then
        wc_mal_ip=$2
        ## 心跳 IP 地址拆分
        for i in ${wc_mal_ip//,/ }; do
          ## 将心跳 IP 加入数组中
          wc_malip_arr+=("$i")
        done
      else
        color_printf red "请检查参数 [-dmi, --wc_mal_ip] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -d | --env_dm_home)
      if [[ $2 ]]; then
        env_dm_home="${2%/}"
      else
        color_printf red "请检查参数 [-d, --env_dm_home] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dd | --data_dir)
      if [[ $2 ]]; then
        data_dir="${2%/}"
      else
        color_printf red "请检查参数 [-dd, --data_dir] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ad | --arch_dir)
      if [[ $2 ]]; then
        arch_dir="${2%/}"
      else
        color_printf red "请检查参数 [-ad, --arch_dir] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -bd | --backup_dir)
      if [[ $2 ]]; then
        backup_dir="${2%/}"
      else
        color_printf red "请检查参数 [-bd, --backup_dir] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dn | --db_name)
      if [[ $2 ]]; then
        db_name=$2
      else
        color_printf red "请检查参数 [-dn, --db_name] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -in | --instance_name)
      if [[ $2 ]]; then
        if [[ $dm_install_mode = "dsc" && ! $2 = "" ]]; then
          color_printf red "dsc不需要传[-in, --instance_name] 参数"
          exit 99
        else
          instance_name=$2
        fi
      else
        color_printf red "请检查参数 [-in, --instance_name] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -es | --extent_size)
      if [[ $2 ]]; then
        extent_size=$2
        if [[ $extent_size != "16" && $extent_size != "32" && $extent_size != "64" ]]; then
          color_printf red "请检查参数 [-es, --extent_size] 的值 $2 是否为 [16|32|64]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-es, --extent_size] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ps | --page_size)
      if [[ $2 ]]; then
        page_size=$2
        if [[ $page_size != "4" && $page_size != "8" && $page_size != "16" && $page_size != "32" ]]; then
          color_printf red "请检查参数 [-ps, --page_size] 的值 $2 是否为 [4|8|16|32]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-ps, --page_size] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -cs | --case_sensitive)
      if [[ $2 ]]; then
        case_sensitive=$2
        if [[ $case_sensitive != "Y" && $case_sensitive != "N" && $case_sensitive != "1" && $case_sensitive != "0" ]]; then
          color_printf red "请检查参数 [-cs, --case_sensitive] 的值 $2 是否为 [Y|N|1|0]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-cs, --case_sensitive] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -c | --charset)
      if [[ $2 ]]; then
        charset=$2
        if [[ $charset != "0" && $charset != "1" && $charset != "2" ]]; then
          color_printf red "请检查参数 [-c, --charset] 的值 $2 是否为 [0|1|2]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-c, --charset] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -lic | --length_in_char)
      if [[ $2 ]]; then
        length_in_char=$2
        if [[ $length_in_char != "Y" && $length_in_char != "N" && $length_in_char != "1" && $length_in_char != "0" ]]; then
          color_printf red "请检查参数 [-lic, --length_in_char] 的值 $2 是否为 [Y|N|1|0]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-lic, --length_in_char] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ls | --log_size)
      if [[ $2 ]]; then
        log_size=$2
        if [ "$log_size" -lt 256 ] || [ "$log_size" -gt 2048 ]; then
          color_printf red "请检查参数 [-ls, --log_size] 的值 $2 是否在 [256~2048] 区间内！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-ls, --log_size] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -er | --enable_arch)
      if [[ $2 ]]; then
        enable_arch=$2
        if [[ $enable_arch != "Y" && $enable_arch != "N" ]]; then
          color_printf red "请检查参数 [-er, --enable_arch] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-er, --enable_arch] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -sl | --space_limit)
      if [[ $2 ]]; then
        space_limit=$2
      else
        color_printf red "请检查参数 [-sl, --space_limit] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -pn | --port_num)
      if [[ $2 ]]; then
        port_num=$2
      else
        color_printf red "请检查参数 [-pn, --port_num] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -sp | --sysdba_pwd)
      if [[ $2 ]]; then
        sysdba_pwd=$2
      else
        color_printf red "请检查参数 [-sp, --sysdba_pwd] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -bm | --backup_mode)
      if [[ $2 ]]; then
        backup_mode=$2
        if [[ $backup_mode != "1" && $backup_mode != "2" ]]; then
          color_printf red "请检查参数 [-bm, --backup_mode] 的值 $2 是否为 [1|2]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-bm, --backup_mode] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ti | --timeserver_ip)
      if [[ $2 ]]; then
        timeserver_ip=$2
      else
        color_printf red "请检查参数 [-ti, --timeserver_ip] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -m | --only_conf_os)
      if [[ $2 ]]; then
        only_conf_os=$2
        if [[ $only_conf_os != "N" && $only_conf_os != "Y" ]]; then
          color_printf red "请检查参数 [-m, --only_conf_os] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-m, --only_conf_os] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ud | --install_until_db)
      if [[ $2 ]]; then
        install_until_db=$2
        if [[ $install_until_db != "N" && $install_until_db != "Y" ]]; then
          color_printf red "请检查参数 [-ud, --install_until_db] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-ud, --install_until_db] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -oid | --only_init_db)
      if [[ $2 ]]; then
        only_init_db=$2
        if [[ $only_init_db != "N" && $only_init_db != "Y" ]]; then
          color_printf red "请检查参数 [-id, --only_init_db] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-id, --only_init_db] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -opd | --optimize_db)
      if [[ $2 ]]; then
        optimize_db=$2
        if [[ $optimize_db != "N" && $optimize_db != "Y" ]]; then
          color_printf red "请检查参数 [-opd, --optimize_db] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-opd, --optimize_db] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -oopd | --only_optimize_db)
      if [[ $2 ]]; then
        only_optimize_db=$2
        if [[ $only_optimize_db != "N" && $only_optimize_db != "Y" ]]; then
          color_printf red "请检查参数 [-oopd, --only_optimize_db] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-oopd, --only_optimize_db] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -mp | --mem_percent)
      if [[ $2 ]]; then
        mem_percent=$2
      else
        color_printf red "请检查参数 [-mp, --mem_percent] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -sm | --sort_mode)
      if [[ $2 ]]; then
        sort_mode=$2
      else
        color_printf red "请检查参数 [-sm, --sort_mode] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -di | --dm_iso_name)
      if [[ $2 ]]; then
        dm_iso_name=$2
      else
        color_printf red "请检查参数 [-di, --dm_iso_name] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -kp | --key_path)
      if [[ $2 ]]; then
        key_path=$2
      else
        color_printf red "请检查参数 [-kp, --key_path] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dgn | --dw_group_name)
      if [[ $2 ]]; then
        dw_group_name=$2
      else
        color_printf red "请检查参数 [-dgn, --dw_group_name] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -mpn | --mal_port_num)
      if [[ $2 ]]; then
        mal_port_num=$2
      else
        color_printf red "请检查参数 [-mpn, --mal_port_num] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -mdpn | --mal_dw_port_num)
      if [[ $2 ]]; then
        mal_dw_port_num=$2
      else
        color_printf red "请检查参数 [-mdpn, --mal_dw_port_num] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -midpn | --mal_inst_dw_port_num)
      if [[ $2 ]]; then
        mal_inst_dw_port_num=$2
      else
        color_printf red "请检查参数 [-midpn, --mal_inst_dw_port_num] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -oguid | --oguid)
      oguid=$2
      shift 2
      ;;
    -dim | --dm_install_mode)
      dm_install_mode=$2
      shift 2
      ;;
    -dmoi | --dw_monitor_ip)
      if [[ $2 ]]; then
        dw_monitor_ip=$2
        ## 如果存在单独监视器，则配置为自动切换模式，否则默认为手动切换模式
        dw_mode=AUTO
      else
        color_printf red "请检查参数 [-dmoi, --dw_monitor_ip] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ddn | --dsc_db_name)
      if [[ $2 ]]; then
        dsc_db_name=$2
      else
        color_printf red "请检查参数 [-ddn, --dsc_db_name] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -den | --dsc_ep_name)
      if [[ $2 ]]; then
        dsc_ep_name=$2
      else
        color_printf red "请检查参数 [-den, --dsc_ep_name] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -fd | --format_disk)
      if [[ $2 ]]; then
        format_disk=$2
        if [[ $format_disk != "Y" && $format_disk != "N" ]]; then
          color_printf red "请检查参数 [-fd, --format_disk] 的值 $2 是否为 [Y|N]！"
          exit 99
        fi
      else
        color_printf red "请检查参数 [-fd, --format_disk] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -dcd | --dcr_disk)
      if [[ $2 ]]; then
        dcr_disk=$2
      else
        color_printf red "请检查参数 [-dcd, --dcr_disk] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -vod | --vote_disk)
      if [[ $2 ]]; then
        vote_disk=$2
      else
        color_printf red "请检查参数 [-vod, --vote_disk] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -lod | --log_disk)
      log_disk=$2
      shift 2
      ;;
    -ard | --arch_disk)
      arch_disk=$2
      shift 2
      ;;
    -dad | --data_disk)
      if [[ $2 ]]; then
        data_disk=$2
      else
        color_printf red "请检查参数 [-dad, --data_disk] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ila | --is_local_arch)
      if [[ $2 ]]; then
        is_local_arch=$2
        if [[ $is_local_arch != "N" && $is_local_arch != "Y" ]]; then
          color_printf red "Sorry, the parameter [--ila, --is_local_arch] $2 is not in [Y|N]"
          exit 99
        fi
      else
        color_printf red "Sorry, the parameter [--ila, --is_local_arch] $2 is null"
        exit 99
      fi
      shift 2
      ;;
    -apd | --asm_pwd)
      if [[ $2 ]]; then
        asm_pwd=$2
      else
        color_printf red "请检查参数 [-apd, --asm_pwd] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -cdp | --css_dcr_ep_port)
      if [[ $2 ]]; then
        css_dcr_ep_port=$2
      else
        color_printf red "请检查参数 [-cdp, --css_dcr_ep_port] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -adp | --asm_dcr_ep_port)
      if [[ $2 ]]; then
        asm_dcr_ep_port=$2
      else
        color_printf red "请检查参数 [-adp, --asm_dcr_ep_port] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -ddp | --db_dcr_ep_port)
      if [[ $2 ]]; then
        db_dcr_ep_port=$2
      else
        color_printf red "请检查参数 [-ddp, --db_dcr_ep_port] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -amp | --asvrmal_mal_port)
      if [[ $2 ]]; then
        asvrmal_mal_port=$2
      else
        color_printf red "请检查参数 [-amp, --asvrmal_mal_port] 的值是否为空！"
        exit 99
      fi
      shift 2
      ;;
    -mtp | --multipath)
      if [[ $2 ]]; then
        multipath=$2
        if [[ $multipath != "N" && $multipath != "Y" ]]; then
          color_printf red "Sorry, the parameter [-mtp, --multipath] $2 is not in [Y|N]"
          exit 99
        fi
      else
        color_printf red "Sorry, the parameter [-mtp, --multipath] $2 is null"
        exit 99
      fi
      shift 2
      ;;
    -fld | --fil_disk)
      if [[ $2 ]]; then
        fil_disk=$2
      else
        color_printf red "Sorry, the parameter [-fld, --fil_disk] $2 is null"
        exit 99
      fi
      shift 2
      ;;
    -h | --help) help ;;
    *)
      color_printf red "脚本已退出安装，请检查是否参数是否正确，可以查看脚本帮助：'DMShellInstall --help' 获取更多信息！"
      exit 99
      ;;
    esac
  done

  ## 切换用户除去显示 last login 信息
  sed -i 's/^session\+[[:space:]]\+include[[:space:]]\+postlogin/#&/g' /etc/pam.d/su

  ## 判断当前目录是否存在 DMInstall.bin 文件，如果不存在且 -di 参数为空，则报错退出
  if [[ $only_optimize_db = "N" && $only_init_db = "N" ]]; then
    if [[ $fil_disk ]]; then
      filter_arr
      check_disk
      exit 99
    elif [[ ! -e $software_dir/DMInstall.bin && ! $dm_iso_name ]]; then
      color_printf red "当前目录不存在达梦安装程序 DMInstall.bin，并且没有指定参数 -di，无法继续安装！"
      exit 99
    fi
  fi

  cat <<-EOF

 ███████   ████     ████  ████████ ██               ██  ██ ██                    ██              ██  ██
░██░░░░██ ░██░██   ██░██ ██░░░░░░ ░██              ░██ ░██░██                   ░██             ░██ ░██
░██    ░██░██░░██ ██ ░██░██       ░██       █████  ░██ ░██░██ ███████   ██████ ██████  ██████   ░██ ░██
░██    ░██░██ ░░███  ░██░█████████░██████  ██░░░██ ░██ ░██░██░░██░░░██ ██░░░░ ░░░██░  ░░░░░░██  ░██ ░██
░██    ░██░██  ░░█   ░██░░░░░░░░██░██░░░██░███████ ░██ ░██░██ ░██  ░██░░█████   ░██    ███████  ░██ ░██
░██    ██ ░██   ░    ░██       ░██░██  ░██░██░░░░  ░██ ░██░██ ░██  ░██ ░░░░░██  ░██   ██░░░░██  ░██ ░██
░███████  ░██        ░██ ████████ ░██  ░██░░██████ ███ ███░██ ███  ░██ ██████   ░░██ ░░████████ ███ ███
░░░░░░░   ░░         ░░ ░░░░░░░░  ░░   ░░  ░░░░░░ ░░░ ░░░ ░░ ░░░   ░░ ░░░░░░     ░░   ░░░░░░░░ ░░░ ░░░ 

EOF
  ## 节点一选择数据库安装模式
  if [[ $node_num = 1 ]]; then
    if [[ -z $dm_install_mode ]]; then
      while :; do
        echo
        color_printf blue "请选择达梦数据库部署类型：单机[si]/数据守护[dw]/dsc集群[dsc] "
        echo
        read -r dm_install_mode
        echo
        if [[ $dm_install_mode = "si" ]]; then
          dm_install_mode=single
        elif [[ $dm_install_mode = "dw" ]]; then
          dm_install_mode=datawatch
        elif [[ $dm_install_mode = "dsc" ]]; then
          dm_install_mode=dsc
        fi
        if [[ $dm_install_mode = "single" || $dm_install_mode = "datawatch" || $dm_install_mode = "dsc" ]]; then
          color_printf blue "达梦数据库安装部署类型:" "$dm_install_mode"
          echo
          break
        else
          color_printf red "达梦数据库部署类型选择错误，请重新输入 si 或者 dw 或者 dsc ！"
          echo
        fi
      done
      ## 校验服务器ip
      if [[ $dm_install_mode = "datawatch" || $dm_install_mode = "dsc" ]]; then
        check_ip_arr "$wc_public_ip" -dpi --wc_public_ip
        check_ip_arr "$wc_mal_ip" -dmi --wc_mal_ip
        if [[ $dw_monitor_ip ]]; then
          check_ip "$dw_monitor_ip" -dmoi --dw_monitor_ip
        fi
      fi
      ##判断传进去的磁盘是否有重复的uuiid值，有的话脚本直接退出，只在一节点执行
      if [[ $dm_install_mode = "dsc" ]]; then
        check_disk -dcd --dcr_disk "$dcr_disk"
        check_disk -vod --vote_disk "$vote_disk"
        if [[ $arch_disk ]]; then
          check_disk -ard --arch_disk "$arch_disk"
        fi
        if [[ $log_disk ]]; then
          check_disk -lod --log_disk "$log_disk"
        fi
        check_disk -dad --data_disk "$data_disk"
      fi
    fi
  fi
  ## 把传进的磁盘加到数组里面
  if [[ $dm_install_mode = "dsc" ]]; then
    disk_arr=("$dcr_disk" "$vote_disk" ${log_disk:+$log_disk} ${arch_disk:+$arch_disk} "$data_disk")
  fi
  ## 安装 DW/DSC 时，多节点互信需要配置 yum 源，判断是否需要挂载操作系统 ISO 镜像，如果需要挂载则自动配置 yum 源；如果不需要挂载，则默认自行配置 yum 源
  if [[ $dm_install_mode = "datawatch" ]]; then
    if [[ $node_num = 1 && $os_iso = "Y" ]]; then
      conf_yum
    fi
    install_dw
  elif [[ $dm_install_mode = "dsc" ]]; then
    if [[ $multipath = "Y" && ${disk_list_arr[0]} =~ "dev" ]]; then
      conf_yum
    elif [[ $node_num = 1 && $os_iso = "Y" ]]; then
      conf_yum
    fi
    if [[ $node_num = 1 ]]; then
      install_dsc
    fi
  fi

  color_printf blue "达梦数据库开始安装："
  ## 主程序入口
  print_sysinfo | tee "$dminstalllog"
  if [[ $only_optimize_db = "N" ]]; then
    if [[ $only_init_db = "N" ]]; then
      conf_swap | tee -a "$dminstalllog"
      conf_firewall | tee -a "$dminstalllog"
      conf_selinux | tee -a "$dminstalllog"
      if [[ $hostname ]]; then
        conf_hostname
      fi
      conf_ntp | tee -a "$dminstalllog"
      conf_grub | tee -a "$dminstalllog"
      create_user | tee -a "$dminstalllog"
      create_dir
      conf_sysctl | tee -a "$dminstalllog"
      conf_limits | tee -a "$dminstalllog"
      conf_ipc | tee -a "$dminstalllog"
      conf_lang | tee -a "$dminstalllog"
      drop_cache | tee -a "$dminstalllog"
      conf_profile | tee -a "$dminstalllog"
      if [[ $dm_iso_name ]]; then
        mount_iso
      fi
      if [[ $only_conf_os = "N" ]]; then
        install_dmdb
        ## 单机数据库
        if [[ $install_until_db = "N" && $dm_install_mode != "dsc" ]]; then
          init_dmdb
          install_dmdb_svc
          start_dmdb
          conf_arch_bak_user
          optimize_dmini
          dm_sqllog
          if [[ $optimize_db = "Y" ]]; then
            optimize_dmdb
          fi
        fi
      fi
      ## 数据守护
      if [[ $dm_install_mode = "datawatch" ]]; then
        if [[ $node_num = 1 ]]; then
          dw_pri_backup
          other_node_shell
          ## 排除掉监视器节点，不需要脱机恢复
        elif [[ $node_num != -1 ]]; then
          dw_std_restore
        fi
        dw_conf_para
        echo
        if [[ $node_num = 1 ]]; then
          echo
          color_printf blue "恭喜您，达梦数据守护已经安装完成！"
          echo
        fi
        ## dsc集群
      elif [[ $dm_install_mode = "dsc" ]]; then
        dsc_conf_disk_arr
        dsc_dmdcr_cfg
        dsc_dmasvmal
        dsc_dmdcr
        dsc_dmcssm
        dsc_dmsvc
        dsc_dmservice
        conf_arch_bak_user
        optimize_dmini
        if [[ $node_num = 1 ]]; then
          other_node_shell
          dsc_dmasmcmd
          dsc_start_ca
          dsc_dmasmtool
          init_dsc_db
          dm_sqllog
          dsc_start
        fi
        if [[ $node_num = 1 ]]; then
          echo
          color_printf blue "恭喜您，达梦数据库DMDSC已经安装完成！"
          echo
        fi
      else
        echo
        color_printf blue "恭喜您，达梦数据库已经安装完成！"
        echo
      fi
    else
      ## 仅初始化数据库实例
      ## 仅创建实例时，判断数据文件路径是否存在，没有则创建
      if [[ -d $data_dir ]]; then
        ## 如果当前初始化的实例存在
        if [[ -d $data_dir/$db_name ]]; then
          ## 首先关闭实例
          su - dmdba -c "$env_dm_home/bin/DmService$db_name stop"
          ## 其次重命名当前需要初始化的实例
          su - dmdba -c "mv $data_dir/$db_name $data_dir/$db_name$current"
        fi
      else
        mkdir "$data_dir"
        chown -R dmdba:dinstall "$backup_dir"
      fi
      init_dmdb
      install_dmdb_svc
      start_dmdb
      conf_arch_bak_user
      optimize_dmini
      dm_sqllog
      if [[ $optimize_db = "Y" ]]; then
        optimize_dmdb
      fi
      echo
      color_printf blue "恭喜您，达梦数据库已经初始化完成！"
      echo
    fi
  else
    ## 脚本仅优化数据库
    optimize_dmdb
  fi
}

main "$@"
